Browse Source

Generalised input framework for raw mice, gamepads, trackers (incl. HMDs like Oculus Rift)

rdb 10 năm trước cách đây
mục cha
commit
bd0683d101
64 tập tin đã thay đổi với 2970 bổ sung1619 xóa
  1. 15 15
      makepanda/makepanda.py
  2. 3 12
      panda/src/device/analogNode.I
  3. 18 7
      panda/src/device/analogNode.cxx
  4. 2 1
      panda/src/device/analogNode.h
  5. 6 20
      panda/src/device/buttonNode.I
  6. 22 20
      panda/src/device/buttonNode.cxx
  7. 2 4
      panda/src/device/buttonNode.h
  8. 0 74
      panda/src/device/clientAnalogDevice.I
  9. 1 46
      panda/src/device/clientAnalogDevice.cxx
  10. 0 23
      panda/src/device/clientAnalogDevice.h
  11. 0 1
      panda/src/device/clientBase.cxx
  12. 0 110
      panda/src/device/clientButtonDevice.I
  13. 2 111
      panda/src/device/clientButtonDevice.cxx
  14. 0 38
      panda/src/device/clientButtonDevice.h
  15. 0 53
      panda/src/device/clientDevice.I
  16. 11 14
      panda/src/device/clientDevice.cxx
  17. 6 21
      panda/src/device/clientDevice.h
  18. 1 13
      panda/src/device/clientTrackerDevice.I
  19. 0 6
      panda/src/device/clientTrackerDevice.h
  20. 9 2
      panda/src/device/config_device.cxx
  21. 3 12
      panda/src/device/dialNode.I
  22. 14 0
      panda/src/device/evdevInputDevice.I
  23. 424 0
      panda/src/device/evdevInputDevice.cxx
  24. 63 0
      panda/src/device/evdevInputDevice.h
  25. 445 0
      panda/src/device/inputDevice.I
  26. 517 0
      panda/src/device/inputDevice.cxx
  27. 279 0
      panda/src/device/inputDevice.h
  28. 27 0
      panda/src/device/inputDeviceManager.I
  29. 240 0
      panda/src/device/inputDeviceManager.cxx
  30. 64 0
      panda/src/device/inputDeviceManager.h
  31. 14 0
      panda/src/device/linuxJoystickDevice.I
  32. 313 0
      panda/src/device/linuxJoystickDevice.cxx
  33. 60 0
      panda/src/device/linuxJoystickDevice.h
  34. 4 1
      panda/src/device/p3device_composite1.cxx
  35. 0 1
      panda/src/device/p3device_composite2.cxx
  36. 1 2
      panda/src/device/trackerNode.I
  37. 9 10
      panda/src/device/trackerNode.cxx
  38. 8 8
      panda/src/device/trackerNode.h
  39. 1 19
      panda/src/display/callbackGraphicsWindow.cxx
  40. 0 1
      panda/src/display/callbackGraphicsWindow.h
  41. 5 1
      panda/src/display/config_display.cxx
  42. 30 87
      panda/src/display/graphicsWindow.cxx
  43. 7 11
      panda/src/display/graphicsWindow.h
  44. 6 226
      panda/src/display/graphicsWindowInputDevice.I
  45. 21 196
      panda/src/display/graphicsWindowInputDevice.cxx
  46. 38 98
      panda/src/display/graphicsWindowInputDevice.h
  47. 13 16
      panda/src/display/mouseAndKeyboard.cxx
  48. 1 2
      panda/src/display/mouseAndKeyboard.h
  49. 1 0
      panda/src/display/p3display_composite2.cxx
  50. 2 0
      panda/src/putil/config_util.cxx
  51. 75 0
      panda/src/putil/gamepadButton.cxx
  52. 57 0
      panda/src/putil/gamepadButton.h
  53. 0 1
      panda/src/putil/p3putil_composite1.cxx
  54. 2 0
      panda/src/putil/p3putil_composite2.cxx
  55. 16 16
      panda/src/tinydisplay/tinyOsxGraphicsWindow.mm
  56. 9 11
      panda/src/tinydisplay/tinySDLGraphicsWindow.cxx
  57. 9 11
      panda/src/tinydisplay/tinyXGraphicsWindow.cxx
  58. 0 2
      panda/src/vrpn/vrpnAnalog.cxx
  59. 0 2
      panda/src/vrpn/vrpnButton.cxx
  60. 0 2
      panda/src/vrpn/vrpnDial.cxx
  61. 12 19
      panda/src/vrpn/vrpnTracker.cxx
  62. 3 3
      panda/src/windisplay/winGraphicsWindow.cxx
  63. 78 269
      panda/src/x11display/x11GraphicsWindow.cxx
  64. 1 1
      panda/src/x11display/x11GraphicsWindow.h

+ 15 - 15
makepanda/makepanda.py

@@ -3641,6 +3641,21 @@ if (not RUNTIME):
   TargetAdd('libp3dgraph.in', opts=['IMOD:panda3d.core', 'ILIB:libp3dgraph', 'SRCDIR:panda/src/dgraph'])
   TargetAdd('libp3dgraph_igate.obj', input='libp3dgraph.in', opts=["DEPENDENCYONLY"])
 
+#
+# DIRECTORY: panda/src/device/
+#
+
+if (not RUNTIME):
+  OPTS=['DIR:panda/src/device', 'BUILDING:PANDA']
+  TargetAdd('p3device_composite1.obj', opts=OPTS, input='p3device_composite1.cxx')
+  TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx')
+
+  OPTS=['DIR:panda/src/device']
+  IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"])
+  TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES)
+  TargetAdd('libp3device.in', opts=['IMOD:panda3d.core', 'ILIB:libp3device', 'SRCDIR:panda/src/device'])
+  TargetAdd('libp3device_igate.obj', input='libp3device.in', opts=["DEPENDENCYONLY"])
+
 #
 # DIRECTORY: panda/src/display/
 #
@@ -3665,21 +3680,6 @@ if (not RUNTIME):
     TargetAdd('subprocessWindowBuffer.obj', opts=OPTS, input='subprocessWindowBuffer.cxx')
     TargetAdd('libp3subprocbuffer.ilb', input='subprocessWindowBuffer.obj')
 
-#
-# DIRECTORY: panda/src/device/
-#
-
-if (not RUNTIME):
-  OPTS=['DIR:panda/src/device', 'BUILDING:PANDA']
-  TargetAdd('p3device_composite1.obj', opts=OPTS, input='p3device_composite1.cxx')
-  TargetAdd('p3device_composite2.obj', opts=OPTS, input='p3device_composite2.cxx')
-
-  OPTS=['DIR:panda/src/device']
-  IGATEFILES=GetDirectoryContents('panda/src/device', ["*.h", "*_composite*.cxx"])
-  TargetAdd('libp3device.in', opts=OPTS, input=IGATEFILES)
-  TargetAdd('libp3device.in', opts=['IMOD:panda3d.core', 'ILIB:libp3device', 'SRCDIR:panda/src/device'])
-  TargetAdd('libp3device_igate.obj', input='libp3device.in', opts=["DEPENDENCYONLY"])
-
 #
 # DIRECTORY: panda/src/pnmtext/
 #

+ 3 - 12
panda/src/device/analogNode.I

@@ -44,10 +44,7 @@ is_valid() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int AnalogNode::
 get_num_controls() const {
-  _analog->acquire();
-  int result = _analog->get_num_controls();
-  _analog->unlock();
-  return result;
+  return _analog->get_num_controls();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -60,10 +57,7 @@ get_num_controls() const {
 ////////////////////////////////////////////////////////////////////
 INLINE double AnalogNode::
 get_control_state(int index) const {
-  _analog->acquire();
-  double result = _analog->get_control_state(index);
-  _analog->unlock();
-  return result;
+  return _analog->get_control_state(index);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -75,10 +69,7 @@ get_control_state(int index) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool AnalogNode::
 is_control_known(int index) const {
-  _analog->acquire();
-  bool result = _analog->is_control_known(index);
-  _analog->unlock();
-  return result;
+  return _analog->is_control_known(index);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 18 - 7
panda/src/device/analogNode.cxx

@@ -30,7 +30,7 @@ AnalogNode(ClientBase *client, const string &device_name) :
   DataNode(device_name)
 {
   _xy_output = define_output("xy", EventStoreVec2::get_class_type());
-  _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
+  _xy = new EventStoreVec2(LPoint2(0));
 
   nassertv(client != (ClientBase *)NULL);
   PT(ClientDevice) device =
@@ -49,7 +49,23 @@ AnalogNode(ClientBase *client, const string &device_name) :
     return;
   }
 
-  _analog = DCAST(ClientAnalogDevice, device);
+  _analog = device;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnalogNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+AnalogNode::
+AnalogNode(InputDevice *device) :
+  DataNode(device->get_name()),
+  _analog(device)
+{
+  _xy_output = define_output("xy", EventStoreVec2::get_class_type());
+  _xy = new EventStoreVec2(LPoint2(0));
+
+  nassertv(device != (InputDevice *)NULL);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -74,9 +90,7 @@ write(ostream &out, int indent_level) const {
   DataNode::write(out, indent_level);
 
   if (_analog != (ClientAnalogDevice *)NULL) {
-    _analog->acquire();
     _analog->write_controls(out, indent_level + 2);
-    _analog->unlock();
   }
 }
 
@@ -100,8 +114,6 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
     _analog->poll();
 
     LPoint2 out(0.0f, 0.0f);
-
-    _analog->acquire();
     for (int i = 0; i < max_outputs; i++) {
       if (_outputs[i]._index >= 0 &&
           _analog->is_control_known(_outputs[i]._index)) {
@@ -112,7 +124,6 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
         }
       }
     }
-    _analog->unlock();
     _xy->set_value(out);
     output.set_data(_xy_output, EventParameter(_xy));
   }

+ 2 - 1
panda/src/device/analogNode.h

@@ -45,6 +45,7 @@
 class EXPCL_PANDA_DEVICE AnalogNode : public DataNode {
 PUBLISHED:
   AnalogNode(ClientBase *client, const string &device_name);
+  AnalogNode(InputDevice *device);
   virtual ~AnalogNode();
 
   INLINE bool is_valid() const;
@@ -73,7 +74,7 @@ private:
   enum { max_outputs = 2 };
   OutputData _outputs[max_outputs];
 
-  PT(ClientAnalogDevice) _analog;
+  PT(InputDevice) _analog;
 
 protected:
   // Inherited from DataNode

+ 6 - 20
panda/src/device/buttonNode.I

@@ -21,7 +21,7 @@
 ////////////////////////////////////////////////////////////////////
 INLINE bool ButtonNode::
 is_valid() const {
-  return (_button != (ClientButtonDevice *)NULL) && _button->is_connected();
+  return (_device != (ClientButtonDevice *)NULL) && _device->is_connected();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -36,10 +36,7 @@ is_valid() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int ButtonNode::
 get_num_buttons() const {
-  _button->acquire();
-  int result = _button->get_num_buttons();
-  _button->unlock();
-  return result;
+  return _device->get_num_buttons();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -58,9 +55,7 @@ get_num_buttons() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void ButtonNode::
 set_button_map(int index, ButtonHandle button) {
-  _button->acquire();
-  _button->set_button_map(index, button);
-  _button->unlock();
+  _device->set_button_map(index, button);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -73,10 +68,7 @@ set_button_map(int index, ButtonHandle button) {
 ////////////////////////////////////////////////////////////////////
 INLINE ButtonHandle ButtonNode::
 get_button_map(int index) const {
-  _button->acquire();
-  ButtonHandle result = _button->get_button_map(index);
-  _button->unlock();
-  return result;
+  return _device->get_button_map(index);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -88,10 +80,7 @@ get_button_map(int index) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool ButtonNode::
 get_button_state(int index) const {
-  _button->acquire();
-  bool result = _button->get_button_state(index);
-  _button->unlock();
-  return result;
+  return _device->get_button_state(index);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -103,8 +92,5 @@ get_button_state(int index) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool ButtonNode::
 is_button_known(int index) const {
-  _button->acquire();
-  bool result = _button->is_button_known(index);
-  _button->unlock();
-  return result;
+  return _device->is_button_known(index);
 }

+ 22 - 20
panda/src/device/buttonNode.cxx

@@ -30,7 +30,6 @@ ButtonNode(ClientBase *client, const string &device_name) :
   DataNode(device_name)
 {
   _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
-  _button_events = new ButtonEventList;
 
   nassertv(client != (ClientBase *)NULL);
   PT(ClientDevice) device =
@@ -49,7 +48,21 @@ ButtonNode(ClientBase *client, const string &device_name) :
     return;
   }
 
-  _button = DCAST(ClientButtonDevice, device);
+  _device = device;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ButtonNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+ButtonNode::
+ButtonNode(InputDevice *device) :
+  DataNode(device->get_name()),
+  _device(device)
+{
+  _button_events_output = define_output("button_events", ButtonEventList::get_class_type());
+  _device = device;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -59,7 +72,7 @@ ButtonNode(ClientBase *client, const string &device_name) :
 ////////////////////////////////////////////////////////////////////
 ButtonNode::
 ~ButtonNode() {
-  // When the _button pointer destructs, the ClientButtonDevice
+  // When the _device pointer destructs, the ClientButtonDevice
   // disconnects itself from the ClientBase, and everything that needs
   // to get turned off does.  Magic.
 }
@@ -73,11 +86,9 @@ void ButtonNode::
 output(ostream &out) const {
   DataNode::output(out);
 
-  if (_button != (ClientButtonDevice *)NULL) {
+  if (_device != (InputDevice *)NULL) {
     out << " (";
-    _button->acquire();
-    _button->output_buttons(out);
-    _button->unlock();
+    _device->output_buttons(out);
     out << ")";
   }
 }
@@ -91,10 +102,8 @@ void ButtonNode::
 write(ostream &out, int indent_level) const {
   DataNode::write(out, indent_level);
 
-  if (_button != (ClientButtonDevice *)NULL) {
-    _button->acquire();
-    _button->write_buttons(out, indent_level + 2);
-    _button->unlock();
+  if (_device != (InputDevice *)NULL) {
+    _device->write_buttons(out, indent_level + 2);
   }
 }
 
@@ -115,14 +124,7 @@ void ButtonNode::
 do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, 
                  DataNodeTransmit &output) {
   if (is_valid()) {
-    _button->poll();
-    _button->acquire();
-
-    (*_button_events) = (*_button->get_button_events());
-
-    _button->get_button_events()->clear();
-    _button->unlock();
-
-    output.set_data(_button_events_output, EventParameter(_button_events));
+    PT(ButtonEventList) bel = _device->get_button_events();
+    output.set_data(_button_events_output, EventParameter(bel));
   }
 }

+ 2 - 4
panda/src/device/buttonNode.h

@@ -20,8 +20,6 @@
 #include "clientBase.h"
 #include "clientButtonDevice.h"
 #include "dataNode.h"
-#include "buttonEventList.h"
-
 
 ////////////////////////////////////////////////////////////////////
 //       Class : ButtonNode
@@ -41,6 +39,7 @@
 class EXPCL_PANDA_DEVICE ButtonNode : public DataNode {
 PUBLISHED:
   ButtonNode(ClientBase *client, const string &device_name);
+  ButtonNode(InputDevice *device);
   virtual ~ButtonNode();
 
   INLINE bool is_valid() const;
@@ -58,7 +57,7 @@ public:
   virtual void write(ostream &out, int indent_level = 0) const;
 
 private:
-  PT(ClientButtonDevice) _button;
+  PT(InputDevice) _device;
 
 protected:
   // Inherited from DataNode
@@ -69,7 +68,6 @@ protected:
 private:
   // outputs
   int _button_events_output;
-  PT(ButtonEventList) _button_events;
 
 public:
   static TypeHandle get_class_type() {

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

@@ -13,18 +13,6 @@
 ////////////////////////////////////////////////////////////////////
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: ClientAnalogDevice::AnalogState::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE ClientAnalogDevice::AnalogState::
-AnalogState() :
-  _state(0.0),
-  _known(false)
-{
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ClientAnalogDevice::Constructor
 //       Access: Protected
@@ -35,65 +23,3 @@ 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 acquire() 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;
-  }
-}

+ 1 - 46
panda/src/device/clientAnalogDevice.cxx

@@ -19,25 +19,6 @@
 
 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
@@ -45,32 +26,6 @@ ensure_control_index(int index) {
 ////////////////////////////////////////////////////////////////////
 void ClientAnalogDevice::
 write(ostream &out, int indent_level) const {
-  indent(out, indent_level) << get_type() << " " << get_device_name() << ":\n";
+  indent(out, indent_level) << get_type() << " " << get_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";
-  }
-}

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

@@ -36,30 +36,7 @@ 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 pvector<AnalogState> Controls;
-  Controls _controls;
-
 
 public:
   static TypeHandle get_class_type() {

+ 0 - 1
panda/src/device/clientBase.cxx

@@ -144,7 +144,6 @@ get_device(TypeHandle device_type, const string &device_name) {
 
   if (device != (ClientDevice *)NULL) {
     dbn.insert(DevicesByName::value_type(device_name, device));
-    device->_is_connected = true;
   }
 
   return device;

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

@@ -12,113 +12,3 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-
-////////////////////////////////////////////////////////////////////
-//     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 ButtonEventList *ClientButtonDevice::
-get_button_events() const {
-  return _button_events;
-}

+ 2 - 111
panda/src/device/clientButtonDevice.cxx

@@ -28,47 +28,6 @@ ClientButtonDevice::
 ClientButtonDevice(ClientBase *client, const string &device_name):
   ClientDevice(client, get_class_type(), device_name)
 {
-  _button_events = new ButtonEventList();
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     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 acquire() 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->add_event(ButtonEvent(handle, down ? ButtonEvent::T_down : ButtonEvent::T_up));
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     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());
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -78,7 +37,7 @@ ensure_button_index(int index) {
 ////////////////////////////////////////////////////////////////////
 void ClientButtonDevice::
 output(ostream &out) const {
-  out << get_type() << " " << get_device_name() << " (";
+  out << get_type() << " " << get_name() << " (";
   output_buttons(out);
   out << ")";
 }
@@ -90,74 +49,6 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 void ClientButtonDevice::
 write(ostream &out, int indent_level) const {
-  indent(out, indent_level) << get_type() << " " << get_device_name() << ":\n";
+  indent(out, indent_level) << get_type() << " " << get_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";
-  }
-}

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

@@ -39,46 +39,9 @@ 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 ButtonEventList *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 pvector<ButtonState> Buttons;
-  Buttons _buttons;
-
-  PT(ButtonEventList) _button_events;
-
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
@@ -95,7 +58,6 @@ public:
 
 private:
   static TypeHandle _type_handle;
-  friend class ButtonState;
 };
 
 #include "clientButtonDevice.I"

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

@@ -24,17 +24,6 @@ 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
@@ -50,45 +39,3 @@ 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::acquire
-//       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::
-acquire() {
-#ifdef OLD_HAVE_IPC
-  _lock.acquire();
-#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 acquire().
-////////////////////////////////////////////////////////////////////
-INLINE void ClientDevice::
-unlock() {
-#ifdef OLD_HAVE_IPC
-  _lock.unlock();
-#endif
-}

+ 11 - 14
panda/src/device/clientDevice.cxx

@@ -27,15 +27,14 @@ TypeHandle ClientDevice::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ClientDevice::
 ClientDevice(ClientBase *client, TypeHandle device_type,
-             const string &device_name) :
+             const string &device_name, int device_flags) :
+  InputDevice(device_name, DC_unknown, device_flags),
   _client(client),
-  _device_type(device_type),
-  _device_name(device_name)
+  _device_type(device_type)
 {
   // 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;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -49,7 +48,7 @@ ClientDevice(ClientBase *client, TypeHandle device_type,
 ////////////////////////////////////////////////////////////////////
 ClientDevice::
 ~ClientDevice() {
-  nassertv(!_is_connected);
+  nassertv(!is_connected());
 
   // And now we explicitly unref the client pointer.
   unref_delete(_client);
@@ -72,19 +71,17 @@ ClientDevice::
 ////////////////////////////////////////////////////////////////////
 void ClientDevice::
 disconnect() {
-  if (_is_connected) {
-    acquire();
+  if (is_connected()) {
     bool disconnected =
-      _client->disconnect_device(_device_type, _device_name, this);
-    _is_connected = false;
-    unlock();
+      _client->disconnect_device(_device_type, get_name(), this);
+    set_connected(false);
     nassertv(disconnected);
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClientDevice::poll
-//       Access: Public
+//     Function: ClientDevice::do_poll
+//       Access: Public, Virtual, Final
 //  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
@@ -94,7 +91,7 @@ disconnect() {
 //               data in this ClientDevice to ensure that it is fresh.
 ////////////////////////////////////////////////////////////////////
 void ClientDevice::
-poll() {
+do_poll() {
   _client->poll();
 }
 
@@ -105,7 +102,7 @@ poll() {
 ////////////////////////////////////////////////////////////////////
 void ClientDevice::
 output(ostream &out) const {
-  out << get_type() << " " << get_device_name();
+  out << get_type() << " " << get_name();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 21
panda/src/device/clientDevice.h

@@ -16,12 +16,7 @@
 #define CLIENTDEVICE_H
 
 #include "pandabase.h"
-
-#include "typedReferenceCount.h"
-
-#ifdef OLD_HAVE_IPC
-#include <ipc_mutex.h>
-#endif
+#include "inputDevice.h"
 
 class ClientBase;
 
@@ -32,24 +27,20 @@ class ClientBase;
 //               This is an abstract interface; the actual
 //               implementations are in ClientTrackerDevice, etc.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_DEVICE ClientDevice : public TypedReferenceCount {
+class EXPCL_PANDA_DEVICE ClientDevice : public InputDevice {
 protected:
   ClientDevice(ClientBase *client, TypeHandle device_type,
-               const string &device_name);
+               const string &device_name, int device_flags=0);
 
 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 acquire();
-  INLINE void unlock();
+  virtual void do_poll() FINAL;
 
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;
@@ -57,21 +48,15 @@ public:
 private:
   ClientBase *_client;
   TypeHandle _device_type;
-  string _device_name;
-  bool _is_connected;
-
-#ifdef OLD_HAVE_IPC
-  mutex _lock;
-#endif
 
 public:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
   static void init_type() {
-    TypedReferenceCount::init_type();
+    InputDevice::init_type();
     register_type(_type_handle, "ClientDevice",
-                  TypedReferenceCount::get_class_type());
+                  InputDevice::get_class_type());
   }
   virtual TypeHandle get_type() const {
     return get_class_type();

+ 1 - 13
panda/src/device/clientTrackerDevice.I

@@ -20,18 +20,6 @@
 ////////////////////////////////////////////////////////////////////
 INLINE ClientTrackerDevice::
 ClientTrackerDevice(ClientBase *client, const string &device_name):
-  ClientDevice(client, get_class_type(), device_name)
+  ClientDevice(client, get_class_type(), device_name, IDF_has_tracker)
 {
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: ClientTrackerDevice::get_data
-//       Access: Public
-//  Description: Returns the TrackerData that this device is
-//               reporting.
-////////////////////////////////////////////////////////////////////
-INLINE const TrackerData &ClientTrackerDevice::
-get_data() const {
-  return _data;
-}
-

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

@@ -30,12 +30,6 @@ class EXPCL_PANDA_DEVICE 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;

+ 9 - 2
panda/src/device/config_device.cxx

@@ -23,7 +23,9 @@
 #include "clientDialDevice.h"
 #include "clientTrackerDevice.h"
 #include "dialNode.h"
-#include "mouseAndKeyboard.h"
+#include "evdevInputDevice.h"
+#include "inputDevice.h"
+#include "linuxJoystickDevice.h"
 #include "trackerNode.h"
 #include "virtualMouse.h"
 
@@ -64,7 +66,12 @@ init_libdevice() {
   ClientDialDevice::init_type();
   ClientTrackerDevice::init_type();
   DialNode::init_type();
-  MouseAndKeyboard::init_type();
+  InputDevice::init_type();
   TrackerNode::init_type();
   VirtualMouse::init_type();
+
+#ifdef PHAVE_LINUX_INPUT_H
+  EvdevInputDevice::init_type();
+  LinuxJoystickDevice::init_type();
+#endif
 }

+ 3 - 12
panda/src/device/dialNode.I

@@ -33,10 +33,7 @@ is_valid() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int DialNode::
 get_num_dials() const {
-  _dial->acquire();
-  int result = _dial->get_num_dials();
-  _dial->unlock();
-  return result;
+  return _dial->get_num_dials();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -49,10 +46,7 @@ get_num_dials() const {
 ////////////////////////////////////////////////////////////////////
 INLINE double DialNode::
 read_dial(int index) {
-  _dial->acquire();
-  double result = _dial->read_dial(index);
-  _dial->unlock();
-  return result;
+  return _dial->read_dial(index);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -64,8 +58,5 @@ read_dial(int index) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool DialNode::
 is_dial_known(int index) const {
-  _dial->acquire();
-  bool result = _dial->is_dial_known(index);
-  _dial->unlock();
-  return result;
+  return _dial->is_dial_known(index);
 }

+ 14 - 0
panda/src/device/evdevInputDevice.I

@@ -0,0 +1,14 @@
+// Filename: evdevInputDevice.I
+// Created by:  rdb (24Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 424 - 0
panda/src/device/evdevInputDevice.cxx

@@ -0,0 +1,424 @@
+// Filename: evdevInputDevice.cxx
+// Created by:  rdb (24Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "evdevInputDevice.h"
+
+#ifdef PHAVE_LINUX_INPUT_H
+
+#include "gamepadButton.h"
+#include "keyboardButton.h"
+#include "mouseButton.h"
+
+#include <fcntl.h>
+#include <linux/input.h>
+
+#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
+
+TypeHandle EvdevInputDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::Constructor
+//       Access: Published
+//  Description: Creates a new device using the Linux joystick
+//               device using the given file descriptor.  It will
+//               be closed when this object destructs.
+////////////////////////////////////////////////////////////////////
+EvdevInputDevice::
+EvdevInputDevice(int fd) :
+  _fd(fd) {
+  init_device();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::Constructor
+//       Access: Published
+//  Description: Creates a new device using the Linux joystick
+//               device using the given device filename.
+////////////////////////////////////////////////////////////////////
+EvdevInputDevice::
+EvdevInputDevice(const string &fn) {
+  _fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK);
+
+  if (_fd >= 0) {
+    init_device();
+  } else {
+    _is_connected = false;
+    device_cat.error()
+      << "Opening raw input device: " << strerror(errno) << " " << fn << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+EvdevInputDevice::
+~EvdevInputDevice() {
+  if (_fd != -1) {
+    close(_fd);
+    _fd = -1;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::do_poll
+//       Access: Private, Virtual
+//  Description: Polls the input device for new activity, to ensure
+//               it contains the latest events.  This will only have
+//               any effect for some types of input devices; others
+//               may be updated automatically, and this method will
+//               be a no-op.
+////////////////////////////////////////////////////////////////////
+void EvdevInputDevice::
+do_poll() {
+  if (_fd != -1) {
+    while (process_events()) {}
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::init_device
+//       Access: Private
+//  Description: Reads basic properties from the device.
+////////////////////////////////////////////////////////////////////
+bool EvdevInputDevice::
+init_device() {
+  uint8_t evtypes[EV_MAX/8 + 1];
+  char name[256];
+  char uniq[256];
+  if (ioctl(_fd, EVIOCGNAME(sizeof(name)), name) < 0 ||
+      ioctl(_fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0 ||
+      ioctl(_fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0) {
+    close(_fd);
+    _fd = -1;
+    _is_connected = false;
+    device_cat.error() << "Opening raw input device: ioctl failed\n";
+    return false;
+  }
+
+  for (char *p=name; *p; p++) {
+    if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
+      *p = '_';
+    }
+  }
+  for (char *p=uniq; *p; p++) {
+    if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
+      *p = '_';
+    }
+  }
+
+  _name = ((string)name) + "." + uniq;
+
+  if (test_bit(EV_REL, evtypes)) {
+    _flags |= IDF_has_pointer;
+  }
+
+  if (test_bit(EV_FF, evtypes)) {
+    _flags |= IDF_has_vibration;
+  }
+
+  _is_connected = true;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::process_events
+//       Access: Private
+//  Description: Reads a number of events from the device.
+////////////////////////////////////////////////////////////////////
+bool EvdevInputDevice::
+process_events() {
+  // Read 8 events at a time.
+  struct input_event events[8];
+
+  int n_read = read(_fd, events, sizeof(events));
+  if (n_read < 0) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      // No data available for now.
+
+    } else if (errno == ENODEV || errno == EINVAL) {
+      // The device ceased to exist, so we better close it.
+      close(_fd);
+      _fd = -1;
+      _is_connected = false;
+      errno = 0;
+
+    } else {
+      device_cat.error() << "read: " << strerror(errno) << "\n";
+    }
+    return false;
+  }
+
+  if (n_read == 0) {
+    return false;
+  }
+
+  n_read /= sizeof(struct input_event);
+
+  int x = _pointer_data.get_x();
+  int y = _pointer_data.get_y();
+  double time = ClockObject::get_global_clock()->get_frame_time();
+  ButtonHandle button;
+
+  for (int i = 0; i < n_read; ++i) {
+    switch (events[i].type) {
+    case EV_SYN:
+      break;
+
+    case EV_REL:
+      if (events[i].code == REL_X) x += events[i].value;
+      if (events[i].code == REL_Y) y += events[i].value;
+      break;
+
+    case EV_ABS:
+      if (events[i].code == ABS_X) x = events[i].value;
+      if (events[i].code == ABS_Y) y = events[i].value;
+      break;
+
+    case EV_KEY:
+      button = map_button(events[i].code);
+      _button_events->add_event(ButtonEvent(button, events[i].value ? ButtonEvent::T_down : ButtonEvent::T_up, time));
+      break;
+
+    default:
+      //cerr << "event " << events[i].type << " - " << events[i].code << " - " << events[i].value << "\n";
+      break;
+    }
+  }
+
+  set_pointer(true, x, y, time);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EvdevInputDevice::map_button
+//       Access: Public, Static
+//  Description: Maps an evdev code to a ButtonHandle.
+////////////////////////////////////////////////////////////////////
+ButtonHandle EvdevInputDevice::
+map_button(int code) {
+  if (code >= 0 && code < 0x80) {
+    // See linux/input.h for the source of this mapping.
+    static const ButtonHandle keyboard_map[] = {
+      ButtonHandle::none(),
+      KeyboardButton::escape(),
+      KeyboardButton::ascii_key('1'),
+      KeyboardButton::ascii_key('2'),
+      KeyboardButton::ascii_key('3'),
+      KeyboardButton::ascii_key('4'),
+      KeyboardButton::ascii_key('5'),
+      KeyboardButton::ascii_key('6'),
+      KeyboardButton::ascii_key('7'),
+      KeyboardButton::ascii_key('8'),
+      KeyboardButton::ascii_key('9'),
+      KeyboardButton::ascii_key('0'),
+      KeyboardButton::ascii_key('-'),
+      KeyboardButton::ascii_key('='),
+      KeyboardButton::backspace(),
+      KeyboardButton::tab(),
+      KeyboardButton::ascii_key('q'),
+      KeyboardButton::ascii_key('w'),
+      KeyboardButton::ascii_key('e'),
+      KeyboardButton::ascii_key('r'),
+      KeyboardButton::ascii_key('t'),
+      KeyboardButton::ascii_key('y'),
+      KeyboardButton::ascii_key('u'),
+      KeyboardButton::ascii_key('i'),
+      KeyboardButton::ascii_key('o'),
+      KeyboardButton::ascii_key('p'),
+      KeyboardButton::ascii_key('['),
+      KeyboardButton::ascii_key(']'),
+      KeyboardButton::enter(),
+      KeyboardButton::lcontrol(),
+      KeyboardButton::ascii_key('a'),
+      KeyboardButton::ascii_key('s'),
+      KeyboardButton::ascii_key('d'),
+      KeyboardButton::ascii_key('f'),
+      KeyboardButton::ascii_key('g'),
+      KeyboardButton::ascii_key('h'),
+      KeyboardButton::ascii_key('j'),
+      KeyboardButton::ascii_key('k'),
+      KeyboardButton::ascii_key('l'),
+      KeyboardButton::ascii_key(';'),
+      KeyboardButton::ascii_key('\''),
+      KeyboardButton::ascii_key('`'),
+      KeyboardButton::lshift(),
+      KeyboardButton::ascii_key('\\'),
+      KeyboardButton::ascii_key('z'),
+      KeyboardButton::ascii_key('x'),
+      KeyboardButton::ascii_key('c'),
+      KeyboardButton::ascii_key('v'),
+      KeyboardButton::ascii_key('b'),
+      KeyboardButton::ascii_key('n'),
+      KeyboardButton::ascii_key('m'),
+      KeyboardButton::ascii_key(','),
+      KeyboardButton::ascii_key('.'),
+      KeyboardButton::ascii_key('/'),
+      KeyboardButton::rshift(),
+      KeyboardButton::ascii_key('*'),
+      KeyboardButton::lalt(),
+      KeyboardButton::space(),
+      KeyboardButton::caps_lock(),
+      KeyboardButton::f1(),
+      KeyboardButton::f2(),
+      KeyboardButton::f3(),
+      KeyboardButton::f4(),
+      KeyboardButton::f5(),
+      KeyboardButton::f6(),
+      KeyboardButton::f7(),
+      KeyboardButton::f8(),
+      KeyboardButton::f9(),
+      KeyboardButton::f10(),
+      KeyboardButton::num_lock(),
+      KeyboardButton::scroll_lock(),
+      KeyboardButton::ascii_key('7'),
+      KeyboardButton::ascii_key('8'),
+      KeyboardButton::ascii_key('9'),
+      KeyboardButton::ascii_key('-'),
+      KeyboardButton::ascii_key('4'),
+      KeyboardButton::ascii_key('5'),
+      KeyboardButton::ascii_key('6'),
+      KeyboardButton::ascii_key('+'),
+      KeyboardButton::ascii_key('1'),
+      KeyboardButton::ascii_key('2'),
+      KeyboardButton::ascii_key('3'),
+      KeyboardButton::ascii_key('0'),
+      KeyboardButton::ascii_key('.'),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      KeyboardButton::f11(),
+      KeyboardButton::f12(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      KeyboardButton::enter(),
+      KeyboardButton::rcontrol(),
+      KeyboardButton::ascii_key('/'),
+      KeyboardButton::print_screen(),
+      KeyboardButton::ralt(),
+      ButtonHandle::none(),
+      KeyboardButton::home(),
+      KeyboardButton::up(),
+      KeyboardButton::page_up(),
+      KeyboardButton::left(),
+      KeyboardButton::right(),
+      KeyboardButton::end(),
+      KeyboardButton::down(),
+      KeyboardButton::page_down(),
+      KeyboardButton::insert(),
+      KeyboardButton::del(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      KeyboardButton::pause(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      ButtonHandle::none(),
+      KeyboardButton::lmeta(),
+      KeyboardButton::rmeta(),
+      KeyboardButton::menu(),
+    };
+    return keyboard_map[code];
+
+  } else if (code < 0x100) {
+    return ButtonHandle::none();
+
+  } else if ((code & 0xfff0) == BTN_MOUSE) {
+    // The number for these is reversed in Panda.
+    if (code == BTN_RIGHT) {
+      return MouseButton::three();
+    } else if (code == BTN_MIDDLE) {
+      return MouseButton::two();
+    } else {
+      return MouseButton::button(code - BTN_MOUSE);
+    }
+  }
+
+  switch (code) {
+  case BTN_A:
+    return GamepadButton::action_a();
+
+  case BTN_B:
+    return GamepadButton::action_b();
+
+  case BTN_C:
+    return GamepadButton::action_c();
+
+  case BTN_X:
+    return GamepadButton::action_x();
+
+  case BTN_Y:
+    return GamepadButton::action_y();
+
+  case BTN_Z:
+    return GamepadButton::action_z();
+
+  case BTN_TL:
+    return GamepadButton::lshoulder();
+
+  case BTN_TR:
+    return GamepadButton::rshoulder();
+
+  case BTN_TL2:
+    return GamepadButton::ltrigger();
+
+  case BTN_TR2:
+    return GamepadButton::rtrigger();
+
+  case BTN_SELECT:
+    return GamepadButton::back();
+
+  case BTN_START:
+    return GamepadButton::start();
+
+  case BTN_MODE:
+    return GamepadButton::guide();
+
+  case BTN_THUMBL:
+    return GamepadButton::lstick();
+
+  case BTN_THUMBR:
+    return GamepadButton::rstick();
+
+  case BTN_TRIGGER_HAPPY1:
+    return GamepadButton::dpad_left();
+
+  case BTN_TRIGGER_HAPPY2:
+    return GamepadButton::dpad_right();
+
+  case BTN_TRIGGER_HAPPY3:
+    return GamepadButton::dpad_up();
+
+  case BTN_TRIGGER_HAPPY4:
+    return GamepadButton::dpad_down();
+
+  default:
+    return ButtonHandle::none();
+  }
+}
+
+#endif

+ 63 - 0
panda/src/device/evdevInputDevice.h

@@ -0,0 +1,63 @@
+// Filename: evdevInputDevice.h
+// Created by:  rdb (24Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EVDEVINPUTDEVICE_H
+#define EVDEVINPUTDEVICE_H
+
+#include "inputDevice.h"
+
+#ifdef PHAVE_LINUX_INPUT_H
+
+////////////////////////////////////////////////////////////////////
+//       Class : EvdevInputDevice
+// Description : This is a type of device that uses the Linux
+//               /dev/input/event# API to read data from a raw mouse.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_DEVICE EvdevInputDevice : public InputDevice {
+PUBLISHED:
+  EvdevInputDevice(int fd);
+  EvdevInputDevice(const string &device);
+  virtual ~EvdevInputDevice();
+
+private:
+  virtual void do_poll();
+
+  bool init_device();
+  bool process_events();
+
+private:
+  int _fd;
+
+public:
+  static ButtonHandle map_button(int code);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    InputDevice::init_type();
+    register_type(_type_handle, "EvdevInputDevice",
+                  InputDevice::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "evdevInputDevice.I"
+
+#endif  // PHAVE_LINUX_INPUT_H
+
+#endif  // EVDEVINPUTDEVICE_H

+ 445 - 0
panda/src/device/inputDevice.I

@@ -0,0 +1,445 @@
+// Filename: inputDevice.I
+// Created by:  drose (24May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::Default Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE InputDevice::
+InputDevice() :
+  _flags(0),
+  _device_class(DC_unknown),
+  _is_connected(false),
+  _event_sequence(0),
+  _enable_pointer_events(false),
+  _battery_level(-1),
+  _max_battery_level(-1)
+{
+  _button_events = new ButtonEventList;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_name
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE string InputDevice::
+get_name() const {
+  LightMutexHolder holder(_lock);
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::is_connected
+//       Access: Public
+//  Description: Returns true if the device is still connected and
+//               able to receive data, false otherwise.  May return
+//               false positives.
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+is_connected() const {
+  LightMutexHolder holder(_lock);
+  return _is_connected;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_device_class
+//       Access: Public
+//  Description: Returns an identification of the general type of
+//               device.  If this could not be determined, returns
+//               DC_unknown.
+////////////////////////////////////////////////////////////////////
+INLINE InputDevice::DeviceClass InputDevice::
+get_device_class() const {
+  LightMutexHolder holder(_lock);
+  return _device_class;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_pointer
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+has_pointer() const {
+  LightMutexHolder holder(_lock);
+  return ((_flags & IDF_has_pointer) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_keyboard
+//       Access: Public
+//  Description: Returns true if the device has a physical keyboard
+//               designed for text entry.
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+has_keyboard() const {
+  LightMutexHolder holder(_lock);
+  return ((_flags & IDF_has_keyboard) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_tracker
+//       Access: Public
+//  Description: Returns true if the device features a tracker that
+//               can track position and/or orientation in 3D space.
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+has_tracker() const {
+  LightMutexHolder holder(_lock);
+  return ((_flags & IDF_has_tracker) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_battery
+//       Access: Public
+//  Description: Returns true if the device may be able to provide
+//               information about its battery life.
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+has_battery() const {
+  LightMutexHolder holder(_lock);
+  return ((_flags & IDF_has_battery) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_axis
+//       Access: Public
+//  Description: Returns true if the device has the given axis.
+////////////////////////////////////////////////////////////////////
+/*
+INLINE bool InputDevice::
+has_axis(Axis axis) const {
+  LightMutexHolder holder(_lock);
+  return ((_has_axes & (1 << axis)) != 0);
+}*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_pointer
+//       Access: Public
+//  Description: Returns the PointerData associated with the input
+//               device's pointer.  This only makes sense if
+//               has_pointer() also returns true.
+////////////////////////////////////////////////////////////////////
+INLINE PointerData InputDevice::
+get_pointer() const {
+  LightMutexHolder holder(_lock);
+  return _pointer_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_tracker
+//       Access: Public
+//  Description: Returns the TrackerData associated with the input
+//               device's tracker.  This only makes sense if
+//               has_tracker() also returns true.
+////////////////////////////////////////////////////////////////////
+INLINE TrackerData InputDevice::
+get_tracker() const {
+  LightMutexHolder holder(_lock);
+  return _tracker_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_battery_level
+//       Access: Public
+//  Description: Returns a rough indication of the battery level,
+//               ranging from 0 (completely empty battery) to the
+//               number reported by get_max_battery_level(), which
+//               represents a full battery.
+//               If this information is not known, returns -1.
+////////////////////////////////////////////////////////////////////
+INLINE short InputDevice::
+get_battery_level() const {
+  LightMutexHolder holder(_lock);
+  return _battery_level;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_max_battery_level
+//       Access: Public
+//  Description: Returns the maximum value that may be reported by
+//               get_battery_level(), representing a full battery.
+//               Returns -1 if no battery information is known.
+////////////////////////////////////////////////////////////////////
+INLINE short InputDevice::
+get_max_battery_level() const {
+  LightMutexHolder holder(_lock);
+  return _max_battery_level;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+get_num_buttons() const {
+  LightMutexHolder holder(_lock);
+  return _buttons.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+set_button_map(int index, ButtonHandle button) {
+  LightMutexHolder holder(_lock);
+  nassertv(index >= 0);
+  if (index >= (int)_buttons.size()) {
+    _buttons.resize(index + 1, ButtonState());
+  }
+
+  _buttons[index]._handle = button;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+get_button_map(int index) const {
+  if (index >= 0 && index < (int)_buttons.size()) {
+    return _buttons[index]._handle;
+  } else {
+    return ButtonHandle::none();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+get_button_state(int index) const {
+  if (index >= 0 && index < (int)_buttons.size()) {
+    return (_buttons[index]._state == S_down);
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+is_button_known(int index) const {
+  if (index >= 0 && index < (int)_buttons.size()) {
+    return _buttons[index]._state != S_unknown;
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_num_controls
+//       Access: Public
+//  Description: Returns the number of analog controls known to the
+//               InputDevice.  This number may change as
+//               more controls are discovered.
+////////////////////////////////////////////////////////////////////
+INLINE int InputDevice::
+get_num_controls() const {
+  return _controls.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_control_map
+//       Access: Public
+//  Description: Associates the indicated ControlAxis with the
+//               control of the indicated index number.  Pass
+//               C_none to turn off any association.
+//
+//               It is not necessary to call this if you simply want
+//               to query the state of the various controls by index
+//               number.
+////////////////////////////////////////////////////////////////////
+INLINE void InputDevice::
+set_control_map(int index, ControlAxis axis) {
+  LightMutexHolder holder(_lock);
+  nassertv(index >= 0);
+  if (index >= (int)_controls.size()) {
+    _controls.resize(index + 1, AnalogState());
+  }
+
+  _controls[index]._axis = axis;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_control_map
+//       Access: Public
+//  Description: Returns the ControlAxis that was previously
+//               associated with the given index number by
+//               a call to set_control_map(), or C_none
+//               if no control was associated.
+////////////////////////////////////////////////////////////////////
+INLINE InputDevice::ControlAxis InputDevice::
+get_control_map(int index) const {
+  if (index >= 0 && index < (int)_controls.size()) {
+    return _controls[index]._axis;
+  } else {
+    return C_none;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+get_control_state(int index) const {
+  if (index >= 0 && index < (int)_controls.size()) {
+    return _controls[index]._state;
+  } else {
+    return 0.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::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 InputDevice::
+is_control_known(int index) const {
+  if (index >= 0 && index < (int)_controls.size()) {
+    return _controls[index]._known;
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::enable_pointer_events
+//       Access: Public
+//  Description: Enables the generation of mouse-movement events.
+////////////////////////////////////////////////////////////////////
+INLINE void InputDevice::
+enable_pointer_events() {
+  LightMutexHolder holder(_lock);
+  _enable_pointer_events = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::disable_pointer_events
+//       Access: Public
+//  Description: Disables the generation of mouse-movement events.
+////////////////////////////////////////////////////////////////////
+INLINE void InputDevice::
+disable_pointer_events() {
+  LightMutexHolder holder(_lock);
+  _enable_pointer_events = false;
+  _pointer_events.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_connected
+//       Access: Protected
+//  Description: Called to indicate that the device has been
+//               disconnected or connected from its host.
+////////////////////////////////////////////////////////////////////
+INLINE void InputDevice::
+set_connected(bool connected) {
+  LightMutexHolder holder(_lock);
+  _is_connected = connected;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::operator ==
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+operator == (const InputDevice &) const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::operator !=
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+operator != (const InputDevice &) const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::operator <
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool InputDevice::
+operator < (const InputDevice &) const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::ButtonState::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE InputDevice::ButtonState::
+ButtonState() :
+  _handle(ButtonHandle::none()),
+  _state(S_unknown)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::AnalogState::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE InputDevice::AnalogState::
+AnalogState() :
+  _axis(C_none),
+  _state(0.0),
+  _known(false)
+{
+}

+ 517 - 0
panda/src/device/inputDevice.cxx

@@ -0,0 +1,517 @@
+// Filename: inputDevice.cxx
+// Created by:  drose (24May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "inputDevice.h"
+
+TypeHandle InputDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::Constructor
+//       Access: Private
+//  Description: Defines a new InputDevice for the window.  Most
+//               windows will have exactly one InputDevice: a
+//               keyboard/mouse pair.  Some may also add joystick
+//               data, or additional mice or something.
+//
+//               This private constructor is only used internally by
+//               the named constructors, below.
+////////////////////////////////////////////////////////////////////
+InputDevice::
+InputDevice(const string &name, DeviceClass dev_class, int flags) :
+  _name(name),
+  _flags(flags),
+  _device_class(dev_class),
+  _is_connected(true),
+  _event_sequence(0),
+  _enable_pointer_events(false),
+  _battery_level(-1),
+  _max_battery_level(-1)
+{
+  _button_events = new ButtonEventList;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+InputDevice::
+InputDevice(const InputDevice &copy) {
+  *this = copy;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+operator = (const InputDevice &copy) {
+  LightMutexHolder holder(_lock);
+  LightMutexHolder holder1(copy._lock);
+  _name = copy._name;
+  _flags = copy._flags;
+  _is_connected = copy._is_connected;
+  _event_sequence = copy._event_sequence;
+  _enable_pointer_events = copy._enable_pointer_events;
+  _pointer_data = copy._pointer_data;
+  _button_events = copy._button_events;
+  _pointer_events = copy._pointer_events;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+InputDevice::
+~InputDevice() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::poll
+//       Access: Public, Virtual
+//  Description: Polls the input device for new activity, to ensure
+//               it contains the latest events.  This will only have
+//               any effect for some types of input devices; others
+//               may be updated automatically, and this method will
+//               be a no-op.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+poll() {
+  LightMutexHolder holder(_lock);
+  do_poll();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_button_event
+//       Access: Public
+//  Description: Returns true if this device has a pending button
+//               event (a mouse button or keyboard button down/up),
+//               false otherwise.  If this returns true, the
+//               particular event may be extracted via
+//               get_button_event().
+////////////////////////////////////////////////////////////////////
+bool InputDevice::
+has_button_event() const {
+  LightMutexHolder holder(_lock);
+  return !_button_events.is_null() && _button_events->get_num_events() > 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_button_events
+//       Access: Public
+//  Description: Returns the list of recently-generated ButtonEvents.
+//               The list is also cleared.
+////////////////////////////////////////////////////////////////////
+PT(ButtonEventList) InputDevice::
+get_button_events() {
+  LightMutexHolder holder(_lock);
+  PT(ButtonEventList) result = new ButtonEventList;
+  swap(_button_events, result);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::has_pointer_event
+//       Access: Public
+//  Description: Returns true if this device has a pending pointer
+//               event (a mouse movement), or false otherwise.  If
+//               this returns true, the particular event may be
+//               extracted via get_pointer_event().
+////////////////////////////////////////////////////////////////////
+bool InputDevice::
+has_pointer_event() const {
+  LightMutexHolder holder(_lock);
+  return (_pointer_events != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::get_pointer_events
+//       Access: Public
+//  Description: Returns a PointerEventList containing all the recent
+//               pointer events.  Clears the list.
+////////////////////////////////////////////////////////////////////
+PT(PointerEventList) InputDevice::
+get_pointer_events() {
+  LightMutexHolder holder(_lock);
+  PT(PointerEventList) result = _pointer_events;
+  _pointer_events.clear();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_pointer
+//       Access: Protected
+//  Description: Records that a mouse movement has taken place.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+set_pointer(bool inwin, double x, double y, double time) {
+  nassertv(_lock.debug_is_locked());
+  _pointer_data._in_window = inwin;
+  _pointer_data._xpos = x;
+  _pointer_data._ypos = y;
+
+  if (_enable_pointer_events) {
+    int seq = _event_sequence++;
+    if (_pointer_events.is_null()) {
+      _pointer_events = new PointerEventList();
+    }
+    _pointer_events->add_event(_pointer_data._in_window,
+                               _pointer_data._xpos,
+                               _pointer_data._ypos,
+                               seq, time);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_pointer_out_of_window
+//       Access: Protected
+//  Description: Records that the mouse pointer has left the window.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+set_pointer_out_of_window(double time) {
+  nassertv(_lock.debug_is_locked());
+  _pointer_data._in_window = false;
+
+  if (_enable_pointer_events) {
+    int seq = _event_sequence++;
+    if (_pointer_events.is_null()) {
+      _pointer_events = new PointerEventList();
+    }
+    _pointer_events->add_event(_pointer_data._in_window,
+                               _pointer_data._xpos,
+                               _pointer_data._ypos,
+                               seq, time);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_button_state
+//       Access: Protected
+//  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 acquire() is in effect while this call is made.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+set_button_state(int index, bool down) {
+  nassertv(_lock.debug_is_locked());
+  nassertv(index >= 0);
+  if (index >= (int)_buttons.size()) {
+    _buttons.resize(index + 1, ButtonState());
+  }
+
+  _buttons[index]._state = down ? S_down : S_up;
+
+  ButtonHandle handle = _buttons[index]._handle;
+  if (handle != ButtonHandle::none()) {
+    _button_events->add_event(ButtonEvent(handle, down ? ButtonEvent::T_down : ButtonEvent::T_up));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_control_state
+//       Access: Protected
+//  Description: Sets the state of the indicated analog index.  The
+//               caller should ensure that acquire() 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.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+set_control_state(int index, double state) {
+  nassertv(_lock.debug_is_locked());
+  nassertv(index >= 0);
+  if (index >= (int)_controls.size()) {
+    _controls.resize(index + 1, AnalogState());
+  }
+
+  _controls[index]._state = state;
+  _controls[index]._known = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::set_tracker
+//       Access: Protected
+//  Description: Records that a tracker movement has taken place.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+set_tracker(const LPoint3 &pos, const LOrientation &orient, double time) {
+  nassertv(_lock.debug_is_locked());
+
+  _tracker_data.set_pos(pos);
+  _tracker_data.set_orient(orient);
+  _tracker_data.set_time(time);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::output
+//       Access: Public
+//  Description: Writes a one-line string describing the device.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+output(ostream &out) const {
+  LightMutexHolder holder(_lock);
+
+  out << _name << " (";
+
+  if (!_is_connected) {
+    out << "dis";
+  }
+
+  out << "connected)";
+
+  if (_device_class != DC_unknown) {
+    out << ", " << _device_class;
+  }
+
+  if (_buttons.size() > 0) {
+    out << ", " << _buttons.size() << " buttons";
+  }
+
+  if (_controls.size() > 0) {
+    out << ", " << _controls.size() << " controls";
+  }
+
+  if (_flags & IDF_has_pointer) {
+    out << ", pointer";
+  }
+  if (_flags & IDF_has_keyboard) {
+    out << ", keyboard";
+  }
+  if (_flags & IDF_has_tracker) {
+    out << ", tracker";
+  }
+  if (_flags & IDF_has_vibration) {
+    out << ", vibration";
+  }
+  if (_flags & IDF_has_battery) {
+    out << ", battery";
+
+    if (_battery_level > 0 && _max_battery_level > 0) {
+      out << " [";
+      short i = 0;
+      for (; i < _battery_level - 1; ++i) {
+        out << '=';
+      }
+      out << '/';
+      for (; i < _max_battery_level; ++i) {
+        out << ' ';
+      }
+      out << ']';
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::output_buttons
+//       Access: Public
+//  Description: Writes a one-line string of all of the current button
+//               states.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+output_buttons(ostream &out) const {
+  LightMutexHolder holder(_lock);
+
+  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: InputDevice::write_buttons
+//       Access: Public
+//  Description: Writes a multi-line description of the current button
+//               states.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+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";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::write_controls
+//       Access: Public
+//  Description: Writes a multi-line description of the current analog
+//               control states.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+write_controls(ostream &out, int indent_level) const {
+  LightMutexHolder holder(_lock);
+
+  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";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDevice::do_poll
+//       Access: Protected, Virtual
+//  Description: Polls the input device for new activity, to ensure
+//               it contains the latest events.  This will only have
+//               any effect for some types of input devices; others
+//               may be updated automatically, and this method will
+//               be a no-op.
+////////////////////////////////////////////////////////////////////
+void InputDevice::
+do_poll() {
+}
+
+ostream &
+operator << (ostream &out, InputDevice::DeviceClass dc) {
+  switch (dc) {
+  case InputDevice::DC_unknown:
+    out << "unknown";
+    break;
+
+  case InputDevice::DC_virtual:
+    out << "virtual";
+    break;
+
+  case InputDevice::DC_keyboard:
+    out << "keyboard";
+    break;
+
+  case InputDevice::DC_mouse:
+    out << "mouse";
+    break;
+
+  case InputDevice::DC_touch:
+    out << "touch";
+    break;
+
+  case InputDevice::DC_gamepad:
+    out << "gamepad";
+    break;
+
+  case InputDevice::DC_flight_stick:
+    out << "flight_stick";
+    break;
+
+  case InputDevice::DC_steering_wheel:
+    out << "steering_wheel";
+    break;
+  }
+  return out;
+}
+
+ostream &
+operator << (ostream &out, InputDevice::ControlAxis axis) {
+  switch (axis) {
+  case InputDevice::C_none:
+    out << "none";
+    break;
+
+  case InputDevice::C_left_x:
+    out << "left_x";
+    break;
+
+  case InputDevice::C_left_y:
+    out << "left_y";
+    break;
+
+  case InputDevice::C_left_trigger:
+    out << "left_trigger";
+    break;
+
+  case InputDevice::C_right_x:
+    out << "right_x";
+    break;
+
+  case InputDevice::C_right_y:
+    out << "right_y";
+    break;
+
+  case InputDevice::C_right_trigger:
+    out << "right_trigger";
+    break;
+
+  case InputDevice::C_x:
+    out << "x";
+    break;
+
+  case InputDevice::C_y:
+    out << "y";
+    break;
+
+  case InputDevice::C_trigger:
+    out << "trigger";
+    break;
+
+  case InputDevice::C_throttle:
+    out << "throttle";
+    break;
+  }
+
+  return out;
+}

+ 279 - 0
panda/src/device/inputDevice.h

@@ -0,0 +1,279 @@
+// Filename: inputDevice.h
+// Created by:  drose (24May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef INPUTDEVICE_H
+#define INPUTDEVICE_H
+
+#include "pandabase.h"
+
+#include "buttonEvent.h"
+#include "buttonEventList.h"
+#include "pointerEvent.h"
+#include "pointerEventList.h"
+#include "mouseData.h"
+#include "trackerData.h"
+#include "clockObject.h"
+
+#include "pdeque.h"
+#include "pvector.h"
+#include "lightMutex.h"
+#include "lightMutexHolder.h"
+
+typedef MouseData PointerData;
+
+////////////////////////////////////////////////////////////////////
+//       Class : InputDevice
+// Description : This is a structure representing a single input
+//               device.  Input devices may have zero or more
+//               buttons, pointers, or controls associated with them,
+//               and optionally a tracker.
+//
+//               These devices are brought under a common interface
+//               because there is such a large range of devices out
+//               there that may support any number of these types of
+//               controls, we couldn't even begin to cover them with
+//               type-specific subclasses.
+//
+//               Use the various has_() and get_num_() methods to
+//               determine information about the device capabilities.
+//               For instance, has_keyboard() will give an indication
+//               that you can receive keystroke events from this
+//               device, and get_num_buttons() will tell you that
+//               the device may send button events.
+//
+//               There is the DeviceType enumeration, however, which
+//               will (if known) contain identification about the
+//               general category of devices this fits in, such as
+//               keyboard, mouse, gamepad, or flight stick.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_DEVICE InputDevice : public TypedReferenceCount {
+PUBLISHED:
+  // This enum contains information that can be used to identify the
+  // type of input device.
+  enum DeviceClass {
+    DC_unknown,
+
+    // This means that the device doesn't correspond to a physical
+    // device, but rather to a dynamic source of input events.
+    DC_virtual,
+
+    // A physical, alphabetical keyboard.
+    DC_keyboard,
+
+    DC_mouse,
+    DC_touch,
+
+    // A gamepad with action buttons, a D-pad, and thumbsticks.
+    DC_gamepad,
+
+    DC_flight_stick,
+    DC_steering_wheel,
+  };
+
+protected:
+  InputDevice(const string &name, DeviceClass dev_class, int flags);
+
+public:
+  InputDevice();
+  InputDevice(const InputDevice &copy);
+  void operator = (const InputDevice &copy);
+  ~InputDevice();
+
+PUBLISHED:
+  enum ControlAxis {
+    C_none,
+
+    // Gamepad
+    C_left_x,
+    C_left_y,
+    C_left_trigger,
+    C_right_x,
+    C_right_y,
+    C_right_trigger,
+
+    // Flight stick
+    C_x,
+    C_y,
+    C_trigger,
+    C_throttle,
+  };
+
+  INLINE string get_name() const;
+  INLINE bool is_connected() const;
+  INLINE DeviceClass get_device_class() const;
+
+  // The human-readable name of this input device.
+  MAKE_PROPERTY(name, get_name);
+
+  // This is false if we know that the device is not currently connected.
+  // May report false positives if we can't know this with certainty.
+  MAKE_PROPERTY(connected, is_connected);
+
+  // This contains an identification of the general type of device.  If
+  // this could not be determined, it is set to DC_unknown.
+  MAKE_PROPERTY(device_class, get_device_class);
+
+  INLINE bool has_pointer() const;
+  INLINE bool has_keyboard() const;
+  INLINE bool has_tracker() const;
+  INLINE bool has_battery() const;
+
+  INLINE PointerData get_pointer() const;
+  INLINE TrackerData get_tracker() const;
+
+  INLINE short get_battery_level() const;
+  INLINE short get_max_battery_level() 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;
+
+  INLINE int get_num_controls() const;
+  INLINE void set_control_map(int index, ControlAxis axis);
+  INLINE ControlAxis get_control_map(int index) const;
+  INLINE double get_control_state(int index) const;
+  INLINE bool is_control_known(int index) const;
+
+  INLINE void enable_pointer_events();
+  INLINE void disable_pointer_events();
+
+  void poll();
+
+  bool has_button_event() const;
+  PT(ButtonEventList) get_button_events();
+  bool has_pointer_event() const;
+  PT(PointerEventList) get_pointer_events();
+
+  virtual void output(ostream &out) const;
+
+protected:
+  void set_pointer(bool inwin, double x, double y, double time);
+  void set_pointer_out_of_window(double time);
+  void set_button_state(int index, bool down);
+  void set_control_state(int index, double state);
+  void set_tracker(const LPoint3 &pos, const LOrientation &orient, double time);
+
+  virtual void do_poll();
+
+public:
+  INLINE void set_connected(bool connected);
+
+  // We need these methods to make VC++ happy when we try to
+  // instantiate a pvector<InputDevice>.  They don't do
+  // anything useful.
+  INLINE bool operator == (const InputDevice &other) const;
+  INLINE bool operator != (const InputDevice &other) const;
+  INLINE bool operator < (const InputDevice &other) const;
+
+  void output_buttons(ostream &out) const;
+  void write_buttons(ostream &out, int indent_level) const;
+  void write_controls(ostream &out, int indent_level) const;
+
+protected:
+  enum InputDeviceFlags {
+    // The device provides absolute screen coordinates.
+    IDF_has_pointer    = 0x01,
+
+    // The device has an interface for providing text input.
+    IDF_has_keyboard   = 0x02,
+
+    // The device has a motion tracker, such as an HMD.
+    IDF_has_tracker    = 0x04,
+
+    // The device can produce force feedback.
+    IDF_has_vibration  = 0x08,
+
+    // The device provides information about battery life.
+    IDF_has_battery    = 0x10,
+  };
+
+protected:
+  typedef pdeque<ButtonEvent> ButtonEvents;
+
+  LightMutex _lock;
+
+  string _name;
+  DeviceClass _device_class;
+  int _flags;
+  int _event_sequence;
+  bool _is_connected;
+  bool _enable_pointer_events;
+  PointerData _pointer_data;
+  PT(ButtonEventList) _button_events;
+  PT(PointerEventList) _pointer_events;
+
+public:
+  enum State {
+    S_unknown,
+    S_up,
+    S_down
+  };
+
+  class ButtonState {
+  public:
+    INLINE ButtonState();
+
+    ButtonHandle _handle;
+    State _state;
+  };
+  typedef pvector<ButtonState> Buttons;
+  Buttons _buttons;
+
+  class AnalogState {
+  public:
+    INLINE AnalogState();
+
+    ControlAxis _axis;
+    double _state;
+    bool _known;
+  };
+  typedef pvector<AnalogState> Controls;
+  Controls _controls;
+
+  short _battery_level;
+  short _max_battery_level;
+
+  TrackerData _tracker_data;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "InputDevice",
+                  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;
+};
+
+INLINE ostream &operator << (ostream &out, const InputDevice &device) {
+  device.output(out);
+  return out;
+}
+
+ostream &operator << (ostream &out, InputDevice::DeviceClass dc);
+ostream &operator << (ostream &out, InputDevice::ControlAxis axis);
+
+#include "inputDevice.I"
+
+#endif

+ 27 - 0
panda/src/device/inputDeviceManager.I

@@ -0,0 +1,27 @@
+// Filename: inputDeviceManager.I
+// Created by:  rdb (09Dec15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::get_global_ptr
+//       Access: Published, Static
+//  Description: Returns the singleton instance.
+////////////////////////////////////////////////////////////////////
+INLINE InputDeviceManager *InputDeviceManager::
+get_global_ptr() {
+  if (_global_ptr == (InputDeviceManager *)NULL) {
+    _global_ptr = new InputDeviceManager;
+  }
+  return _global_ptr;
+}

+ 240 - 0
panda/src/device/inputDeviceManager.cxx

@@ -0,0 +1,240 @@
+// Filename: inputDeviceManager.cxx
+// Created by:  rdb (09Dec15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "inputDeviceManager.h"
+#include "linuxJoystickDevice.h"
+#include "throw_event.h"
+
+#ifdef PHAVE_LINUX_INPUT_H
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#endif
+
+InputDeviceManager *InputDeviceManager::_global_ptr = NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::Constructor
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+InputDeviceManager::
+InputDeviceManager() : _inotify_fd(-1) {
+#ifdef PHAVE_LINUX_INPUT_H
+  // Scan /dev/input for a list of input devices.
+  DIR *dir = opendir("/dev/input");
+  if (dir) {
+    dirent *entry = readdir(dir);
+    while (entry != NULL) {
+      if (entry->d_type == DT_CHR) {
+        string name(entry->d_name);
+        if (!consider_add_linux_device(name)) {
+          // We can't access it.  That's pretty normal for most devices.
+          if (device_cat.is_debug()) {
+            device_cat.debug()
+              << "Ignoring input device /dev/input/" << name << ": "
+              << strerror(errno) << "\n";
+          }
+          errno = 0;
+        }
+      }
+      entry = readdir(dir);
+    }
+    closedir(dir);
+  } else {
+    device_cat.error()
+      << "Error opening directory /dev/input: " << strerror(errno) << "\n";
+    return;
+  }
+
+  // Use inotify to watch /dev/input for hotplugging of devices.
+  _inotify_fd = inotify_init1(O_NONBLOCK);
+
+  if (_inotify_fd < 0) {
+    device_cat.error()
+      << "Error initializing inotify: " << strerror(errno) << "\n";
+
+  } else if (inotify_add_watch(_inotify_fd, "/dev/input", IN_CREATE | IN_ATTRIB | IN_DELETE) < 0) {
+    device_cat.error()
+      << "Error adding inotify watch on /dev/input: " << strerror(errno) << "\n";
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::Destructor
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+InputDeviceManager::
+~InputDeviceManager() {
+#ifdef PHAVE_LINUX_INPUT_H
+  if (_inotify_fd >= 0) {
+    close(_inotify_fd);
+    _inotify_fd = -1;
+  }
+#endif
+}
+
+#ifdef PHAVE_LINUX_INPUT_H
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::consider_add_linux_device
+//       Access: Private
+//  Description: Checks whether the given device is accessible, and
+//               if so, adds it.  Returns false on error.
+////////////////////////////////////////////////////////////////////
+bool InputDeviceManager::
+consider_add_linux_device(const string &name) {
+  // Get the full path name first.
+  string path = "/dev/input/";
+  path += name;
+
+  if (access(path.c_str(), R_OK) < 0) {
+    return false;
+  }
+
+  if (_devices_by_path.count(name)) {
+    // We already have this device.
+    return true;
+  }
+
+  // Check if it's a joystick or game controller device.
+  if (name.size() > 2 && name[0] == 'j' && name[1] == 's' && isdigit(name[2])) {
+    PT(InputDevice) device = new LinuxJoystickDevice(path);
+    if (device_cat.is_info()) {
+      device_cat.info()
+        << "Discovered input device " << *device << "\n";
+    }
+
+    _devices_by_path[name] = device;
+    _all_devices.push_back(MOVE(device));
+    return true;
+  }
+
+  return true;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::add_device
+//       Access: Public
+//  Description: Called when a new device has been discovered.
+////////////////////////////////////////////////////////////////////
+void InputDeviceManager::
+add_device(InputDevice *device) {
+  {
+    LightMutexHolder holder(_lock);
+    _all_devices.push_back(device);
+  }
+  throw_event("device-added", device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::remove_device
+//       Access: Public
+//  Description: Called when a device has been removed.
+////////////////////////////////////////////////////////////////////
+void InputDeviceManager::
+remove_device(InputDevice *device) {
+  {
+    LightMutexHolder holder(_lock);
+
+    InputDevices::iterator it;
+    it = std::find(_all_devices.begin(), _all_devices.end(), device);
+    nassertv_always(it != _all_devices.end());
+
+    _all_devices.erase(it);
+  }
+
+  throw_event("device-removed", device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InputDeviceManager::poll
+//       Access: Public
+//  Description: Polls the system to see if there are any new devices.
+////////////////////////////////////////////////////////////////////
+void InputDeviceManager::
+poll() {
+#ifdef PHAVE_LINUX_INPUT_H
+  // We use inotify to tell us whether a device was added, removed,
+  // or has changed permissions to allow us to access it.
+  unsigned int avail;
+  ioctl(_inotify_fd, FIONREAD, &avail);
+  if (avail == 0) {
+    return;
+  }
+
+  char buffer[avail];
+  int n_read = read(_inotify_fd, buffer, avail);
+  if (n_read < 0) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      // No data available for now.
+
+    } else {
+      device_cat.error() << "read: " << strerror(errno) << "\n";
+    }
+    return;
+  }
+
+  LightMutexHolder holder(_lock);
+
+  // Iterate over the events in the buffer.
+  char *ptr = buffer;
+  char *end = buffer + avail;
+  while (ptr < end) {
+    inotify_event *event = (inotify_event *)ptr;
+
+    string name(event->name);
+
+    if (event->mask & IN_DELETE) {
+      // The device was deleted.  If we have it, remove it.
+      DevicesByPath::iterator it = _devices_by_path.find(name);
+      if (it != _devices_by_path.end()) {
+        PT(InputDevice) device = it->second;
+        device->set_connected(false);
+
+        InputDevices::iterator it2;
+        _devices_by_path.erase(it);
+        it2 = std::find(_all_devices.begin(), _all_devices.end(), device);
+        _all_devices.erase(it2);
+
+        if (device_cat.is_info()) {
+          device_cat.info()
+            << "Removed input device " << *device << "\n";
+        }
+        throw_event("device-removed", device.p());
+      }
+
+    } else if (event->mask & (IN_CREATE | IN_ATTRIB)) {
+      // The device was created, or it was chmodded to be accessible.
+      DevicesByPath::iterator it = _devices_by_path.find(name);
+      if (it == _devices_by_path.end()) {
+        // We don't know about this device yet.
+        if (!consider_add_linux_device(name) && (event->mask & IN_CREATE) != 0) {
+          if (device_cat.is_debug()) {
+            device_cat.debug()
+              << "Ignoring input device /dev/input/" << name << ": "
+              << strerror(errno) << "\n";
+          }
+        }
+        errno = 0;
+      }
+    }
+
+    ptr += sizeof(inotify_event) + event->len;
+  }
+#endif
+}

+ 64 - 0
panda/src/device/inputDeviceManager.h

@@ -0,0 +1,64 @@
+// Filename: inputDeviceManager.h
+// Created by:  rdb (09Dec15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef INPUTDEVICEMANAGER_H
+#define INPUTDEVICEMANAGER_H
+
+#include "pandabase.h"
+#include "lightMutex.h"
+#include "inputDevice.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : InputDeviceManager
+// Description : This class keeps track of all the devices on a
+//               system, and sends out events when a device has been
+//               hot-plugged.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_DEVICE InputDeviceManager {
+private:
+  InputDeviceManager();
+  ~InputDeviceManager();
+
+#ifdef PHAVE_LINUX_INPUT_H
+  bool consider_add_linux_device(const string &name);
+#endif
+
+public:
+  void add_device(InputDevice *device);
+  void remove_device(InputDevice *device);
+
+PUBLISHED:
+  void poll();
+
+  INLINE static InputDeviceManager *get_global_ptr();
+
+private:
+  LightMutex _lock;
+
+#ifdef PHAVE_LINUX_INPUT_H
+  int _inotify_fd;
+
+  typedef pmap<string, InputDevice*> DevicesByPath;
+  DevicesByPath _devices_by_path;
+#endif
+
+  typedef pvector<PT(InputDevice)> InputDevices;
+  InputDevices _all_devices;
+
+  static InputDeviceManager *_global_ptr;
+};
+
+#include "inputDeviceManager.I"
+
+#endif

+ 14 - 0
panda/src/device/linuxJoystickDevice.I

@@ -0,0 +1,14 @@
+// Filename: linuxJoystickDevice.I
+// Created by:  rdb (21Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 313 - 0
panda/src/device/linuxJoystickDevice.cxx

@@ -0,0 +1,313 @@
+// Filename: linuxJoystickDevice.cxx
+// Created by:  rdb (21Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "linuxJoystickDevice.h"
+
+#ifdef PHAVE_LINUX_INPUT_H
+
+#include "gamepadButton.h"
+
+#include <fcntl.h>
+#include <linux/joystick.h>
+
+TypeHandle LinuxJoystickDevice::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: LinuxJoystickDevice::Constructor
+//       Access: Private
+//  Description: Creates a new device using the Linux joystick
+//               device using the given device filename.
+////////////////////////////////////////////////////////////////////
+LinuxJoystickDevice::
+LinuxJoystickDevice(const string &device) :
+  _fd(-1),
+  _device(device)
+{
+  LightMutexHolder holder(_lock);
+  if (!open_device()) {
+    device_cat.error()
+      << "Could not open joystick device " << _device
+      << ": " << strerror(errno) << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LinuxJoystickDevice::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+LinuxJoystickDevice::
+~LinuxJoystickDevice() {
+  if (_fd != -1) {
+    close(_fd);
+    _fd = -1;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LinuxJoystickDevice::do_poll
+//       Access: Private, Virtual
+//  Description: Polls the input device for new activity, to ensure
+//               it contains the latest events.  This will only have
+//               any effect for some types of input devices; others
+//               may be updated automatically, and this method will
+//               be a no-op.
+////////////////////////////////////////////////////////////////////
+void LinuxJoystickDevice::
+do_poll() {
+  if (_fd != -1) {
+    while (process_events()) {}
+  } else {
+    open_device();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LinuxJoystickDevice::open_device
+//       Access: Private
+//  Description: Opens or reopens the joystick device, and reads out
+//               the button and axis mappings.
+//               Assumes the lock has been held.
+////////////////////////////////////////////////////////////////////
+bool LinuxJoystickDevice::
+open_device() {
+  nassertr(_lock.debug_is_locked(), false);
+
+  _fd = open(_device.c_str(), O_RDONLY | O_NONBLOCK);
+
+  if (_fd == -1) {
+    _is_connected = false;
+    return false;
+  }
+
+  // Read the name from the device.
+  char name[128];
+  name[0] = 0;
+  ioctl(_fd, JSIOCGNAME(sizeof(name)), name);
+  _name = name;
+
+  // Get the number of axes.
+  PN_uint8 num_axes = 0, num_buttons = 0;
+  ioctl(_fd, JSIOCGAXES, &num_axes);
+  ioctl(_fd, JSIOCGBUTTONS, &num_buttons);
+
+  _buttons.resize(num_buttons);
+  _controls.resize(num_axes);
+
+  if (num_buttons > 0) {
+    PN_uint16 btnmap[512];
+    ioctl(_fd, JSIOCGBTNMAP, btnmap);
+
+    for (char i = 0; i < num_buttons; ++i) {
+      ButtonHandle handle = ButtonHandle::none();
+      switch (btnmap[i]) {
+      case BTN_A:
+        handle = GamepadButton::action_a();
+        _device_class = DC_gamepad;
+        break;
+
+      case BTN_B:
+        handle = GamepadButton::action_b();
+        break;
+
+      case BTN_C:
+        handle = GamepadButton::action_c();
+        break;
+
+      case BTN_X:
+        handle = GamepadButton::action_x();
+        break;
+
+      case BTN_Y:
+        handle = GamepadButton::action_y();
+        break;
+
+      case BTN_Z:
+        handle = GamepadButton::action_z();
+        break;
+
+      case BTN_TL:
+        handle = GamepadButton::lshoulder();
+        break;
+
+      case BTN_TR:
+        handle = GamepadButton::rshoulder();
+        break;
+
+      case BTN_TL2:
+        handle = GamepadButton::ltrigger();
+        break;
+
+      case BTN_TR2:
+        handle = GamepadButton::rtrigger();
+        break;
+
+      case BTN_SELECT:
+        handle = GamepadButton::back();
+        break;
+
+      case BTN_START:
+        handle = GamepadButton::start();
+        break;
+
+      case BTN_MODE:
+        handle = GamepadButton::guide();
+        break;
+
+      case BTN_THUMBL:
+        handle = GamepadButton::lstick();
+        break;
+
+      case BTN_THUMBR:
+        handle = GamepadButton::rstick();
+        break;
+
+      case BTN_TRIGGER_HAPPY1:
+        handle = GamepadButton::dpad_left();
+        break;
+
+      case BTN_TRIGGER_HAPPY2:
+        handle = GamepadButton::dpad_right();
+        break;
+
+      case BTN_TRIGGER_HAPPY3:
+        handle = GamepadButton::dpad_up();
+        break;
+
+      case BTN_TRIGGER_HAPPY4:
+        handle = GamepadButton::dpad_down();
+        break;
+
+      default:
+        handle = ButtonHandle::none();
+        break;
+      }
+      _buttons[i]._handle = handle;
+    }
+  }
+
+  if (num_axes > 0) {
+    PN_uint8 axmap[512];
+    ioctl(_fd, JSIOCGAXMAP, axmap);
+
+    for (char i = 0; i < num_axes; ++i) {
+      ControlAxis axis = C_none;
+
+      switch (axmap[i]) {
+      case ABS_X:
+        if (_device_class == DC_gamepad) {
+          axis = C_left_x;
+        } else {
+          axis = C_x;
+        }
+        break;
+
+      case ABS_Y:
+        if (_device_class == DC_gamepad) {
+          axis = C_left_y;
+        } else {
+          axis = C_y;
+        }
+        break;
+
+      case ABS_Z:
+        if (_device_class == DC_gamepad) {
+          axis = C_left_trigger;
+        } else {
+          axis = C_trigger;
+        }
+        break;
+
+      case ABS_RX:
+        axis = C_right_x;
+        break;
+
+      case ABS_RY:
+        axis = C_right_y;
+        break;
+
+      case ABS_RZ:
+        axis = C_right_trigger;
+        break;
+
+      default:
+        axis = C_none;
+        break;
+      }
+      _controls[i]._axis = axis;
+    }
+  }
+
+  // Read the init events.
+  _is_connected = true;
+  while (process_events()) {};
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LinuxJoystickDevice::process_events
+//       Access: Private
+//  Description: Reads a number of events from the joystick.
+////////////////////////////////////////////////////////////////////
+bool LinuxJoystickDevice::
+process_events() {
+  // Read 8 events at a time.
+  struct js_event events[8];
+
+  int n_read = read(_fd, events, sizeof(events));
+  if (n_read < 0) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      // No data available for now.
+
+    } else if (errno == ENODEV) {
+      // The device ceased to exist, so we better close it.
+      close(_fd);
+      _fd = -1;
+      _is_connected = false;
+      errno = 0;
+
+    } else {
+      device_cat.error() << "read: " << strerror(errno) << "\n";
+    }
+    return false;
+  }
+
+  if (n_read == 0) {
+    return false;
+  }
+
+  n_read /= sizeof(struct js_event);
+
+  for (int i = 0; i < n_read; ++i) {
+    int index = events[i].number;
+
+    if (events[i].type & JS_EVENT_BUTTON) {
+      set_button_state(index, (events[i].value != 0));
+
+    } else if (events[i].type & JS_EVENT_AXIS) {
+      ControlAxis axis = _controls[index]._axis;
+
+      if (axis == C_left_trigger || axis == C_right_trigger || axis == C_trigger) {
+        // We'd like to use 0.0 to indicate the resting position.
+        set_control_state(index, (events[i].value + 32767) / 65534.0);
+      } else {
+        set_control_state(index, events[i].value / 32767.0);
+      }
+    }
+  }
+
+  return true;
+}
+
+#endif

+ 60 - 0
panda/src/device/linuxJoystickDevice.h

@@ -0,0 +1,60 @@
+// Filename: linuxJoystickDevice.h
+// Created by:  rdb (21Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef LINUXJOYSTICKDEVICE_H
+#define LINUXJOYSTICKDEVICE_H
+
+#include "inputDevice.h"
+
+#ifdef PHAVE_LINUX_INPUT_H
+
+////////////////////////////////////////////////////////////////////
+//       Class : LinuxJoystickDevice
+// Description : This is a type of device that uses the Linux
+//               /dev/input/js# API to read data from a game controller.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_DEVICE LinuxJoystickDevice : public InputDevice {
+PUBLISHED:
+  LinuxJoystickDevice(const string &device);
+  virtual ~LinuxJoystickDevice();
+
+private:
+  virtual void do_poll();
+
+  bool open_device();
+  bool process_events();
+
+private:
+  int _fd;
+  string _device;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    InputDevice::init_type();
+    register_type(_type_handle, "LinuxJoystickDevice",
+                  InputDevice::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "linuxJoystickDevice.I"
+
+#endif  // PHAVE_LINUX_INPUT_H
+
+#endif  // LINUXJOYSTICKDEVICE_H

+ 4 - 1
panda/src/device/p3device_composite1.cxx

@@ -5,4 +5,7 @@
 #include "clientDevice.cxx"
 #include "clientDialDevice.cxx"
 #include "clientTrackerDevice.cxx"
-
+#include "evdevInputDevice.cxx"
+#include "inputDevice.cxx"
+#include "inputDeviceManager.cxx"
+#include "linuxJoystickDevice.cxx"

+ 0 - 1
panda/src/device/p3device_composite2.cxx

@@ -3,7 +3,6 @@
 #include "analogNode.cxx"
 #include "buttonNode.cxx"
 #include "dialNode.cxx"
-#include "mouseAndKeyboard.cxx"
 #include "trackerData.cxx"
 #include "trackerNode.cxx"
 #include "virtualMouse.cxx"

+ 1 - 2
panda/src/device/trackerNode.I

@@ -20,10 +20,9 @@
 ////////////////////////////////////////////////////////////////////
 INLINE bool TrackerNode::
 is_valid() const {
-  return (_tracker != (ClientTrackerDevice *)NULL) && _tracker->is_connected();
+  return (!_tracker.is_null()/* && _tracker->is_connected()*/);
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: TrackerNode::get_pos
 //       Access: Public

+ 9 - 10
panda/src/device/trackerNode.cxx

@@ -51,7 +51,7 @@ TrackerNode(ClientBase *client, const string &device_name) :
     return;
   }
 
-  _tracker = DCAST(ClientTrackerDevice, device);
+  _tracker = device;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -60,18 +60,19 @@ TrackerNode(ClientBase *client, const string &device_name) :
 //  Description:
 ////////////////////////////////////////////////////////////////////
 TrackerNode::
-TrackerNode(ClientTrackerDevice *device) :
-  DataNode(device->get_device_name()),
+TrackerNode(InputDevice *device) :
+  DataNode(device->get_name()),
   _tracker(device)
 {
   _transform_output = define_output("transform", TransformState::get_class_type());
 
   _transform = TransformState::make_identity();
 
-  nassertv(device != (ClientTrackerDevice *)NULL);
-  ClientBase *client = device->get_client();
-  nassertv(client != (ClientBase *)NULL);
-  set_tracker_coordinate_system(client->get_coordinate_system());
+  nassertv(device != (InputDevice *)NULL);
+  nassertv(device->has_tracker());
+
+  //TODO: get coordinate system from tracker object?
+  set_tracker_coordinate_system(CS_default);
   set_graph_coordinate_system(CS_default);
 }
 
@@ -105,9 +106,7 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
                  DataNodeTransmit &output) {
   if (is_valid()) {
     _tracker->poll();
-    _tracker->acquire();
-    _data = _tracker->get_data();
-    _tracker->unlock();
+    _data = _tracker->get_tracker();
 
     _data.get_orient().extract_to_matrix(_mat);
     if (_tracker_cs != _graph_cs) {

+ 8 - 8
panda/src/device/trackerNode.h

@@ -19,7 +19,7 @@
 
 #include "clientBase.h"
 #include "trackerData.h"
-#include "clientTrackerDevice.h"
+#include "inputDevice.h"
 #include "dataNode.h"
 #include "luse.h"
 #include "linmath_events.h"
@@ -27,16 +27,16 @@
 
 ////////////////////////////////////////////////////////////////////
 //       Class : TrackerNode
-// Description : This is the primary interface to a Tracker object
-//               associated with a ClientBase.  It reads the position
-//               and orientation information from the tracker and
-//               makes it available as a transformation on the data
-//               graph.
+// Description : This class reads the position and orientation
+//               information from a tracker device and makes it
+//               available as a transformation on the data graph.
+//               It is also the primary interface to a Tracker object
+//               associated with a ClientBase.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_DEVICE TrackerNode : public DataNode {
 PUBLISHED:
   TrackerNode(ClientBase *client, const string &device_name);
-  TrackerNode(ClientTrackerDevice *device);
+  TrackerNode(InputDevice *device);
   virtual ~TrackerNode();
 
   INLINE bool is_valid() const;
@@ -66,7 +66,7 @@ private:
   CPT(TransformState) _transform;
 
 private:
-  PT(ClientTrackerDevice) _tracker;
+  PT(InputDevice) _tracker;
   TrackerData _data;
   LMatrix4 _mat;
   CoordinateSystem _tracker_cs, _graph_cs;

+ 1 - 19
panda/src/display/callbackGraphicsWindow.cxx

@@ -54,22 +54,6 @@ CallbackGraphicsWindow::
 ~CallbackGraphicsWindow() {
 }
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: CallbackGraphicsWindow::get_input_device
-//       Access: Published
-//  Description: Returns a writable reference to the nth input device
-//               (mouse).  This is intended to be used for the window
-//               implementation to record mouse and keyboard input
-//               information for the Panda system.
-////////////////////////////////////////////////////////////////////
-GraphicsWindowInputDevice &CallbackGraphicsWindow::
-get_input_device(int device) {
-  LightMutexHolder holder(_input_lock);
-  nassertr(device >= 0 && device < (int)_input_devices.size(), _input_devices[0]);
-  return _input_devices[device];
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CallbackGraphicsWindow::create_input_device
 //       Access: Published
@@ -79,9 +63,7 @@ get_input_device(int device) {
 ////////////////////////////////////////////////////////////////////
 int CallbackGraphicsWindow::
 create_input_device(const string &name) {
-  GraphicsWindowInputDevice device =
-    GraphicsWindowInputDevice::pointer_and_keyboard(this, name);
-  return add_input_device(device);
+  return add_input_device(GraphicsWindowInputDevice::pointer_and_keyboard(this, name));
 }
 
 ////////////////////////////////////////////////////////////////////

+ 0 - 1
panda/src/display/callbackGraphicsWindow.h

@@ -182,7 +182,6 @@ PUBLISHED:
   INLINE void clear_render_callback();
   INLINE CallbackObject *get_render_callback() const;
 
-  GraphicsWindowInputDevice &get_input_device(int device);
   int create_input_device(const string &name);
 
 public:

+ 5 - 1
panda/src/display/config_display.cxx

@@ -22,9 +22,11 @@
 #include "graphicsPipe.h"
 #include "graphicsOutput.h"
 #include "graphicsBuffer.h"
-#include "graphicsWindow.h"
 #include "graphicsDevice.h"
+#include "graphicsWindow.h"
+#include "graphicsWindowInputDevice.h"
 #include "graphicsWindowProcCallbackData.h"
+#include "mouseAndKeyboard.h"
 #include "nativeWindowHandle.h"
 #include "parasiteBuffer.h"
 #include "pandaSystem.h"
@@ -501,7 +503,9 @@ init_libdisplay() {
   GraphicsPipe::init_type();
   GraphicsStateGuardian::init_type();
   GraphicsWindow::init_type();
+  GraphicsWindowInputDevice::init_type();
   GraphicsWindowProcCallbackData::init_type();
+  MouseAndKeyboard::init_type();
   NativeWindowHandle::init_type();
   ParasiteBuffer::init_type();
   StandardMunger::init_type();

+ 30 - 87
panda/src/display/graphicsWindow.cxx

@@ -288,6 +288,20 @@ get_num_input_devices() const {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsWindow::get_input_device
+//       Access: Published
+//  Description: Returns the nth input device associated with the
+//               window.  Typically, a window will have exactly one
+//               input device: the keyboard/mouse pair.
+////////////////////////////////////////////////////////////////////
+InputDevice *GraphicsWindow::
+get_input_device(int device) const {
+  LightMutexHolder holder(_input_lock);
+  nassertr(device >= 0 && device < (int)_input_devices.size(), NULL);
+  return _input_devices[device];
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindow::get_input_device_name
 //       Access: Published
@@ -299,7 +313,7 @@ get_input_device_name(int device) const {
   {
     LightMutexHolder holder(_input_lock);
     nassertr(device >= 0 && device < (int)_input_devices.size(), "");
-    result = _input_devices[device].get_name();
+    result = _input_devices[device]->get_name();
   }
   return result;
 }
@@ -317,7 +331,7 @@ has_pointer(int device) const {
   {
     LightMutexHolder holder(_input_lock);
     nassertr(device >= 0 && device < (int)_input_devices.size(), false);
-    result = _input_devices[device].has_pointer();
+    result = _input_devices[device]->has_pointer();
   }
   return result;
 }
@@ -334,7 +348,7 @@ has_keyboard(int device) const {
   {
     LightMutexHolder holder(_input_lock);
     nassertr(device >= 0 && device < (int)_input_devices.size(), false);
-    result = _input_devices[device].has_keyboard();
+    result = _input_devices[device]->has_keyboard();
   }
   return result;
 }
@@ -359,7 +373,7 @@ void GraphicsWindow::
 enable_pointer_events(int device) {
   LightMutexHolder holder(_input_lock);
   nassertv(device >= 0 && device < (int)_input_devices.size());
-  _input_devices[device].enable_pointer_events();
+  _input_devices[device]->enable_pointer_events();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -371,7 +385,7 @@ void GraphicsWindow::
 disable_pointer_events(int device) {
   LightMutexHolder holder(_input_lock);
   nassertv(device >= 0 && device < (int)_input_devices.size());
-  _input_devices[device].disable_pointer_events();
+  _input_devices[device]->disable_pointer_events();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -379,11 +393,11 @@ disable_pointer_events(int device) {
 //       Access: Published
 //  Description: See GraphicsWindowInputDevice::enable_pointer_mode
 ////////////////////////////////////////////////////////////////////
-void GraphicsWindow::
+/*void GraphicsWindow::
 enable_pointer_mode(int device, double speed) {
   LightMutexHolder holder(_input_lock);
   nassertv(device >= 0 && device < (int)_input_devices.size());
-  _input_devices[device].enable_pointer_mode(speed);
+  _input_devices[device]->enable_pointer_mode(speed);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -395,8 +409,8 @@ void GraphicsWindow::
 disable_pointer_mode(int device) {
   LightMutexHolder holder(_input_lock);
   nassertv(device >= 0 && device < (int)_input_devices.size());
-  _input_devices[device].disable_pointer_mode();
-}
+  _input_devices[device]->disable_pointer_mode();
+}*/
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindow::get_pointer
@@ -412,7 +426,7 @@ get_pointer(int device) const {
   {
     LightMutexHolder holder(_input_lock);
     nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
-    result = _input_devices[device].get_pointer();
+    result = _input_devices[device]->get_pointer();
   }
   return result;
 }
@@ -443,81 +457,6 @@ close_ime() {
   return;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::has_button_event
-//       Access: Public
-//  Description: Returns true if the indicated device has a pending
-//               button event (a mouse button or keyboard button
-//               down/up), false otherwise.  If this returns true, the
-//               particular event may be extracted via
-//               get_button_event().
-////////////////////////////////////////////////////////////////////
-bool GraphicsWindow::
-has_button_event(int device) const {
-  bool result;
-  {
-    LightMutexHolder holder(_input_lock);
-    nassertr(device >= 0 && device < (int)_input_devices.size(), false);
-    result = _input_devices[device].has_button_event();
-  }
-  return result;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::get_button_event
-//       Access: Public
-//  Description: Assuming a previous call to has_button_event()
-//               returned true, this returns the pending button event.
-////////////////////////////////////////////////////////////////////
-ButtonEvent GraphicsWindow::
-get_button_event(int device) {
-  ButtonEvent result;
-  {
-    LightMutexHolder holder(_input_lock);
-    nassertr(device >= 0 && device < (int)_input_devices.size(), ButtonEvent());
-    nassertr(_input_devices[device].has_button_event(), ButtonEvent());
-    result = _input_devices[device].get_button_event();
-  }
-  return result;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::has_pointer_event
-//       Access: Public
-//  Description: Returns true if the indicated device has a pending
-//               pointer event (a mouse movement).  If this returns
-//               true, the particular event may be extracted via
-//               get_pointer_events().
-////////////////////////////////////////////////////////////////////
-bool GraphicsWindow::
-has_pointer_event(int device) const {
-  bool result;
-  {
-    LightMutexHolder holder(_input_lock);
-    nassertr(device >= 0 && device < (int)_input_devices.size(), false);
-    result = _input_devices[device].has_pointer_event();
-  }
-  return result;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindow::get_pointer_events
-//       Access: Public
-//  Description: Assuming a previous call to has_pointer_event()
-//               returned true, this returns the pending pointer event list.
-////////////////////////////////////////////////////////////////////
-PT(PointerEventList) GraphicsWindow::
-get_pointer_events(int device) {
-  PT(PointerEventList) result;
-  {
-    LightMutexHolder holder(_input_lock);
-    nassertr(device >= 0 && device < (int)_input_devices.size(), NULL);
-    nassertr(_input_devices[device].has_pointer_event(), NULL);
-    result = _input_devices[device].get_pointer_events();
-  }
-  return result;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindow::verify_window_sizes
 //       Access: Public, Virtual
@@ -860,11 +799,15 @@ system_changed_size(int x_size, int y_size) {
 //               Returns the index of the new device.
 ////////////////////////////////////////////////////////////////////
 int GraphicsWindow::
-add_input_device(const GraphicsWindowInputDevice &device) {
+add_input_device(InputDevice *device) {
   LightMutexHolder holder(_input_lock);
   int index = (int)_input_devices.size();
   _input_devices.push_back(device);
-  _input_devices.back().set_device_index(index);
+
+  if (device->is_of_type(GraphicsWindowInputDevice::get_class_type())) {
+    ((GraphicsWindowInputDevice *)device)->set_device_index(index);
+  }
+
   return index;
 }
 

+ 7 - 11
panda/src/display/graphicsWindow.h

@@ -82,10 +82,12 @@ PUBLISHED:
 
   INLINE WindowHandle *get_window_handle() const;
   MAKE_PROPERTY(window_handle, get_window_handle);
-  
+
   // Mouse and keyboard routines
   int get_num_input_devices() const;
+  InputDevice *get_input_device(int i) const;
   string get_input_device_name(int device) const;
+  MAKE_SEQ(get_input_devices, get_num_input_devices, get_input_device);
   MAKE_SEQ(get_input_device_names, get_num_input_devices, get_input_device_name);
   bool has_pointer(int device) const;
   bool has_keyboard(int device) const;
@@ -93,20 +95,14 @@ PUBLISHED:
 
   void enable_pointer_events(int device);
   void disable_pointer_events(int device);
-  void enable_pointer_mode(int device, double speed);
-  void disable_pointer_mode(int device);
+  /*void enable_pointer_mode(int device, double speed);
+  void disable_pointer_mode(int device);*/
 
   MouseData get_pointer(int device) const;
   virtual bool move_pointer(int device, int x, int y);
   virtual void close_ime();
 
 public:
-  // No need to publish these.
-  bool has_button_event(int device) const;
-  ButtonEvent get_button_event(int device);
-  bool has_pointer_event(int device) const;
-  PT(PointerEventList) get_pointer_events(int device);
-
   virtual void add_window_proc( const GraphicsWindowProc* wnd_proc_object ){};
   virtual void remove_window_proc( const GraphicsWindowProc* wnd_proc_object ){};
   virtual void clear_window_procs(){};
@@ -146,8 +142,8 @@ protected:
   void system_changed_size(int x_size, int y_size);
 
 protected:
-  int add_input_device(const GraphicsWindowInputDevice &device);
-  typedef vector_GraphicsWindowInputDevice InputDevices;
+  int add_input_device(InputDevice *device);
+  typedef pvector<PT(InputDevice)> InputDevices;
   InputDevices _input_devices;
   LightMutex _input_lock;
 

+ 6 - 226
panda/src/display/graphicsWindowInputDevice.I

@@ -12,73 +12,14 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindowInputDevice::Default Constructor
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE GraphicsWindowInputDevice::
-GraphicsWindowInputDevice() {
-  LightMutexHolder holder(_lock);
-  _flags = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::get_name
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE string GraphicsWindowInputDevice::
-get_name() const {
-  LightMutexHolder holder(_lock);
-  return _name;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::has_pointer
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsWindowInputDevice::
-has_pointer() const {
-  LightMutexHolder holder(_lock);
-  return ((_flags & IDF_has_pointer) != 0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::has_keyboard
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsWindowInputDevice::
-has_keyboard() const {
-  LightMutexHolder holder(_lock);
-  return ((_flags & IDF_has_keyboard) != 0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::get_pointer
-//       Access: Public
-//  Description: Returns the MouseData associated with the input
-//               device's pointer.
-////////////////////////////////////////////////////////////////////
-INLINE  MouseData GraphicsWindowInputDevice::
-get_pointer() const {
-  LightMutexHolder holder(_lock);
-  return _mouse_data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::get_raw_pointer
-//       Access: Public
-//  Description: Returns the MouseData associated with the input
-//               device's pointer, in raw form (ie, prior to any
-//               pointer_mode interpretation).
-////////////////////////////////////////////////////////////////////
-INLINE  MouseData GraphicsWindowInputDevice::
-get_raw_pointer() const {
-  LightMutexHolder holder(_lock);
-  return _true_mouse_data;
+GraphicsWindowInputDevice() : _host(NULL) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -90,140 +31,9 @@ get_raw_pointer() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GraphicsWindowInputDevice::
 set_device_index(int index) {
-  LightMutexHolder holder(_lock);
   _device_index = index;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::enable_pointer_events
-//       Access: Public
-//  Description: Enables the generation of mouse-movement events.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-enable_pointer_events() {
-  LightMutexHolder holder(_lock);
-  _enable_pointer_events = true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::disable_pointer_events
-//       Access: Public
-//  Description: Disables the generation of mouse-movement events.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-disable_pointer_events() {
-  LightMutexHolder holder(_lock);
-  _enable_pointer_events = false;
-  _pointer_events.clear();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::button_down
-//       Access: Published
-//  Description: Records that the indicated button has been depressed.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-button_down(ButtonHandle button) {
-  button_down(button, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::button_resume_down
-//       Access: Published
-//  Description: Records that the indicated button was depressed
-//               earlier, and we only just detected the event after
-//               the fact.  This is mainly useful for tracking the
-//               state of modifier keys.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-button_resume_down(ButtonHandle button) {
-  button_resume_down(button, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::button_up
-//       Access: Published
-//  Description: Records that the indicated button has been released.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-button_up(ButtonHandle button) {
-  button_up(button, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::keystroke
-//       Access: Published
-//  Description: Records that the indicated keystroke has been
-//               generated.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-keystroke(int keycode) {
-  keystroke(keycode, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::focus_lost
-//       Access: Published
-//  Description: This should be called when the window focus is lost,
-//               so that we may miss upcoming button events
-//               (especially "up" events) for the next period of time.
-//               It generates keyboard and mouse "up" events for those
-//               buttons that we previously sent unpaired "down"
-//               events, so that the Panda application will believe
-//               all buttons are now released.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-focus_lost() {
-  focus_lost(ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::raw_button_down
-//       Access: Published
-//  Description: Records that the indicated button has been depressed.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-raw_button_down(ButtonHandle button) {
-  raw_button_down(button, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::raw_button_up
-//       Access: Published
-//  Description: Records that the indicated button has been released.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-raw_button_up(ButtonHandle button) {
-  raw_button_up(button, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::set_pointer_in_window
-//       Access: Published
-//  Description: To be called by a particular kind of GraphicsWindow
-//               to indicate that the pointer is within the window, at
-//               the given pixel coordinates.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-set_pointer_in_window(double x, double y) {
-  // mutex is handled in set pointer .. convience function
-  set_pointer(true, x, y, ClockObject::get_global_clock()->get_frame_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::set_pointer_out_of_window
-//       Access: Published
-//  Description: To be called by a particular kind of GraphicsWindow
-//               to indicate that the pointer is no longer within the
-//               window.
-////////////////////////////////////////////////////////////////////
-INLINE void GraphicsWindowInputDevice::
-set_pointer_out_of_window() {
- // mutex is handled in set pointer .. convience function
-  set_pointer(false, _mouse_data._xpos, _mouse_data._ypos,
-              ClockObject::get_global_clock()->get_frame_time());
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindowInputDevice::set_pointer_in_window
 //       Access: Published
@@ -233,8 +43,8 @@ set_pointer_out_of_window() {
 ////////////////////////////////////////////////////////////////////
 INLINE void GraphicsWindowInputDevice::
 set_pointer_in_window(double x, double y, double time) {
- // mutex is handled in set pointer .. convience function
-  set_pointer(true, x, y, time);
+  LightMutexHolder holder(_lock);
+  InputDevice::set_pointer(true, x, y, time);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -246,36 +56,6 @@ set_pointer_in_window(double x, double y, double time) {
 ////////////////////////////////////////////////////////////////////
 INLINE void GraphicsWindowInputDevice::
 set_pointer_out_of_window(double time) {
- // mutex is handled in set pointer .. convience function
-  set_pointer(false, _mouse_data._xpos, _mouse_data._ypos, time);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::operator ==
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsWindowInputDevice::
-operator == (const GraphicsWindowInputDevice &) const {
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::operator !=
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsWindowInputDevice::
-operator != (const GraphicsWindowInputDevice &) const {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::operator <
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE bool GraphicsWindowInputDevice::
-operator < (const GraphicsWindowInputDevice &) const {
-  return false;
+  LightMutexHolder holder(_lock);
+  InputDevice::set_pointer_out_of_window(time);
 }

+ 21 - 196
panda/src/display/graphicsWindowInputDevice.cxx

@@ -18,17 +18,7 @@
 #include "mouseButton.h"
 #include "keyboardButton.h"
 
-#define EXPCL EXPCL_PANDA_DISPLAY
-#define EXPTP EXPTP_PANDA_DISPLAY
-#define TYPE GraphicsWindowInputDevice
-#define NAME vector_GraphicsWindowInputDevice
-
-#include "vector_src.cxx"
-
-// Tell GCC that we'll take care of the instantiation explicitly here.
-#ifdef __GNUC__
-#pragma implementation
-#endif
+TypeHandle GraphicsWindowInputDevice::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindowInputDevice::Constructor
@@ -43,14 +33,8 @@
 ////////////////////////////////////////////////////////////////////
 GraphicsWindowInputDevice::
 GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, int flags) :
-  _host(host),
-  _name(name),
-  _flags(flags),
-  _device_index(0),
-  _event_sequence(0),
-  _pointer_mode_enable(false),
-  _pointer_speed(1.0),
-  _enable_pointer_events(false)
+  InputDevice(name, DC_virtual, flags),
+  _host(host)
 {
 }
 
@@ -60,9 +44,9 @@ GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, int flags) :
 //  Description: This named constructor returns an input device that
 //               only has a pointing device, no keyboard.
 ////////////////////////////////////////////////////////////////////
-GraphicsWindowInputDevice GraphicsWindowInputDevice::
+PT(GraphicsWindowInputDevice) GraphicsWindowInputDevice::
 pointer_only(GraphicsWindow *host, const string &name) {
-  return GraphicsWindowInputDevice(host, name, IDF_has_pointer);
+  return new GraphicsWindowInputDevice(host, name, IDF_has_pointer);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -71,9 +55,9 @@ pointer_only(GraphicsWindow *host, const string &name) {
 //  Description: This named constructor returns an input device that
 //               only has a keyboard, no pointing device.
 ////////////////////////////////////////////////////////////////////
-GraphicsWindowInputDevice GraphicsWindowInputDevice::
+PT(GraphicsWindowInputDevice) GraphicsWindowInputDevice::
 keyboard_only(GraphicsWindow *host, const string &name) {
-  return GraphicsWindowInputDevice(host, name, IDF_has_keyboard);
+  return new GraphicsWindowInputDevice(host, name, IDF_has_keyboard);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -82,9 +66,9 @@ keyboard_only(GraphicsWindow *host, const string &name) {
 //  Description: This named constructor returns an input device that
 //               has both a keyboard and pointer.
 ////////////////////////////////////////////////////////////////////
-GraphicsWindowInputDevice GraphicsWindowInputDevice::
+PT(GraphicsWindowInputDevice) GraphicsWindowInputDevice::
 pointer_and_keyboard(GraphicsWindow *host, const string &name) {
-  return
+  return new
     GraphicsWindowInputDevice(host, name, IDF_has_pointer | IDF_has_keyboard);
 }
 
@@ -94,9 +78,8 @@ pointer_and_keyboard(GraphicsWindow *host, const string &name) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 GraphicsWindowInputDevice::
-GraphicsWindowInputDevice(const GraphicsWindowInputDevice &copy)
-{
-    *this = copy;
+GraphicsWindowInputDevice(const GraphicsWindowInputDevice &copy) {
+  *this = copy;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -105,22 +88,9 @@ GraphicsWindowInputDevice(const GraphicsWindowInputDevice &copy)
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void GraphicsWindowInputDevice::
-operator = (const GraphicsWindowInputDevice &copy)
-{
-  LightMutexHolder holder(_lock);
-  LightMutexHolder holder1(copy._lock);
+operator = (const GraphicsWindowInputDevice &copy) {
   _host = copy._host;
-  _name = copy._name;
-  _flags = copy._flags;
-  _device_index = copy._device_index;
-  _event_sequence = copy._event_sequence;
-  _pointer_mode_enable = copy._pointer_mode_enable;
-  _pointer_speed = copy._pointer_speed;
-  _enable_pointer_events = copy._enable_pointer_events;
-  _mouse_data = copy._mouse_data;
-  _true_mouse_data = copy._true_mouse_data;
-  _button_events = copy._button_events;
-  _pointer_events = copy._pointer_events;
+  InputDevice::operator = (copy);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -132,151 +102,6 @@ GraphicsWindowInputDevice::
 ~GraphicsWindowInputDevice() {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::has_button_event
-//       Access: Public
-//  Description: Returns true if this device has a pending button
-//               event (a mouse button or keyboard button down/up),
-//               false otherwise.  If this returns true, the
-//               particular event may be extracted via
-//               get_button_event().
-////////////////////////////////////////////////////////////////////
-bool GraphicsWindowInputDevice::
-has_button_event() const {
-  LightMutexHolder holder(_lock);
-  return !_button_events.empty();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::get_button_event
-//       Access: Public
-//  Description: Assuming a previous call to has_button_event()
-//               returned true, this returns the pending button event.
-////////////////////////////////////////////////////////////////////
-ButtonEvent GraphicsWindowInputDevice::
-get_button_event() {
-  LightMutexHolder holder(_lock);
-  ButtonEvent be = _button_events.front();
-  _button_events.pop_front();
-  return be;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::has_pointer_event
-//       Access: Public
-//  Description: Returns true if this device has a pending pointer
-//               event (a mouse movement), or false otherwise.  If
-//               this returns true, the particular event may be
-//               extracted via get_pointer_event().
-////////////////////////////////////////////////////////////////////
-bool GraphicsWindowInputDevice::
-has_pointer_event() const {
-  LightMutexHolder holder(_lock);
-  return (_pointer_events != 0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::get_pointer_events
-//       Access: Public
-//  Description: Returns a PointerEventList containing all the recent
-//               pointer events.
-////////////////////////////////////////////////////////////////////
-PT(PointerEventList) GraphicsWindowInputDevice::
-get_pointer_events() {
-  LightMutexHolder holder(_lock);
-  PT(PointerEventList) result = _pointer_events;
-  _pointer_events = 0;
-  return result;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::enable_pointer_mode
-//       Access: Public
-//  Description: There are two modes: raw mode, and pointer mode.
-//               In pointer mode, the mouse stops when it reaches the
-//               edges of the window.  In raw mode, the mouse ignores
-//               the screen boundaries and can continue indefinitely,
-//               even into negative coordinates.  In raw mode, each
-//               "blip" from the mouse hardware corresponds to a
-//               change of 1 unit in the mouse's (x,y) coordinate.
-//               In pointer mode, a variety of speed adjustment factors
-//               and concepts like "mouse acceleration" may be applied.
-//
-//               Mouse zero represents the system mouse pointer.  This
-//               is by definition a pointer, not a raw mouse.  It is
-//               an error to try to enable or disable pointer mode on
-//               mouse zero.
-////////////////////////////////////////////////////////////////////
-void GraphicsWindowInputDevice::
-enable_pointer_mode(double speed) {
-  LightMutexHolder holder(_lock);
-  nassertv(_device_index != 0);
-  _pointer_mode_enable = true;
-  _pointer_speed = speed;
-  _mouse_data._xpos = _host->get_x_size() * 0.5;
-  _mouse_data._ypos = _host->get_y_size() * 0.5;
-  _mouse_data._in_window = true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::disable_pointer_mode
-//       Access: Public
-//  Description: see enable_pointer_mode.
-////////////////////////////////////////////////////////////////////
-void GraphicsWindowInputDevice::
-disable_pointer_mode() {
-  LightMutexHolder holder(_lock);
-  nassertv(_device_index != 0);
-  _pointer_mode_enable = false;
-  _pointer_speed = 1.0;
-  _mouse_data = _true_mouse_data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsWindowInputDevice::set_pointer
-//       Access: Published
-//  Description: Records that a mouse movement has taken place.
-////////////////////////////////////////////////////////////////////
-void GraphicsWindowInputDevice::
-set_pointer(bool inwin, double x, double y, double time) {
-  LightMutexHolder holder(_lock);
-
-  double delta_x = x - _true_mouse_data._xpos;
-  double delta_y = y - _true_mouse_data._ypos;
-  _true_mouse_data._in_window = inwin;
-  _true_mouse_data._xpos = x;
-  _true_mouse_data._ypos = y;
-
-  if (_pointer_mode_enable) {
-    double pointer_x = _mouse_data._xpos;
-    double pointer_y = _mouse_data._ypos;
-    pointer_x += (delta_x * _pointer_speed);
-    pointer_y += (delta_y * _pointer_speed);
-    double xhi = _host->get_x_size();
-    double yhi = _host->get_y_size();
-    if (pointer_x < 0.0) pointer_x = 0.0;
-    if (pointer_y < 0.0) pointer_y = 0.0;
-    if (pointer_x > xhi) pointer_x = xhi;
-    if (pointer_y > yhi) pointer_y = yhi;
-    _mouse_data._in_window = true;
-    _mouse_data._xpos = pointer_x;
-    _mouse_data._ypos = pointer_y;
-  } else {
-    _mouse_data = _true_mouse_data;
-  }
-
-  if (_enable_pointer_events) {
-    int seq = _event_sequence++;
-    if (_pointer_events == 0) {
-      _pointer_events = new PointerEventList();
-    }
-    _pointer_events->add_event(_mouse_data._in_window,
-                               _mouse_data._xpos,
-                               _mouse_data._ypos,
-                               seq, time);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsWindowInputDevice::button_down
 //       Access: Published
@@ -285,7 +110,7 @@ set_pointer(bool inwin, double x, double y, double time) {
 void GraphicsWindowInputDevice::
 button_down(ButtonHandle button, double time) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_down, time));
+  _button_events->add_event(ButtonEvent(button, ButtonEvent::T_down, time));
   _buttons_held.insert(button);
 }
 
@@ -300,7 +125,7 @@ button_down(ButtonHandle button, double time) {
 void GraphicsWindowInputDevice::
 button_resume_down(ButtonHandle button, double time) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_resume_down, time));
+  _button_events->add_event(ButtonEvent(button, ButtonEvent::T_resume_down, time));
   _buttons_held.insert(button);
 }
 
@@ -312,7 +137,7 @@ button_resume_down(ButtonHandle button, double time) {
 void GraphicsWindowInputDevice::
 button_up(ButtonHandle button, double time) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_up, time));
+  _button_events->add_event(ButtonEvent(button, ButtonEvent::T_up, time));
   _buttons_held.erase(button);
 }
 
@@ -325,7 +150,7 @@ button_up(ButtonHandle button, double time) {
 void GraphicsWindowInputDevice::
 keystroke(int keycode, double time) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(keycode, time));
+  _button_events->add_event(ButtonEvent(keycode, time));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -340,7 +165,7 @@ void GraphicsWindowInputDevice::
 candidate(const wstring &candidate_string, size_t highlight_start,
           size_t highlight_end, size_t cursor_pos) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(candidate_string,
+  _button_events->add_event(ButtonEvent(candidate_string,
                                        highlight_start, highlight_end,
                                        cursor_pos));
 }
@@ -361,7 +186,7 @@ focus_lost(double time) {
   LightMutexHolder holder(_lock);
   ButtonsHeld::iterator bi;
   for (bi = _buttons_held.begin(); bi != _buttons_held.end(); ++bi) {
-    _button_events.push_back(ButtonEvent(*bi, ButtonEvent::T_up, time));
+    _button_events->add_event(ButtonEvent(*bi, ButtonEvent::T_up, time));
   }
   _buttons_held.clear();
 }
@@ -374,7 +199,7 @@ focus_lost(double time) {
 void GraphicsWindowInputDevice::
 raw_button_down(ButtonHandle button, double time) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_raw_down, time));
+  _button_events->add_event(ButtonEvent(button, ButtonEvent::T_raw_down, time));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -385,5 +210,5 @@ raw_button_down(ButtonHandle button, double time) {
 void GraphicsWindowInputDevice::
 raw_button_up(ButtonHandle button, double time) {
   LightMutexHolder holder(_lock);
-  _button_events.push_back(ButtonEvent(button, ButtonEvent::T_raw_up, time));
+  _button_events->add_event(ButtonEvent(button, ButtonEvent::T_raw_up, time));
 }

+ 38 - 98
panda/src/display/graphicsWindowInputDevice.h

@@ -16,139 +16,79 @@
 #define GRAPHICSWINDOWINPUTDEVICE_H
 
 #include "pandabase.h"
-
-#include "buttonEvent.h"
-#include "pointerEvent.h"
-#include "pointerEventList.h"
-#include "mouseData.h"
-#include "clockObject.h"
-
-#include "pdeque.h"
-#include "pvector.h"
-#include "lightMutex.h"
-#include "lightMutexHolder.h"
+#include "inputDevice.h"
 
 // Forward declarations
 class GraphicsWindow;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : GraphicsWindowInputDevice
-// Description : This is a structure representing a single input
-//               device that may be associated with a window.
-//               Typically this will be a keyboard/mouse pair, and
-//               there will be exactly one of these associated with
-//               each window, but other variants are possible.
+// Description : This is a virtual input device that represents
+//               the keyboard and mouse pair that is associated with
+//               a particular window.  It collects mouse and keyboard
+//               events from the windowing system while the window is
+//               in focus.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_DISPLAY GraphicsWindowInputDevice {
+class EXPCL_PANDA_DISPLAY GraphicsWindowInputDevice : public InputDevice {
 private:
   GraphicsWindowInputDevice(GraphicsWindow *host, const string &name, int flags);
 
 public:
-  static GraphicsWindowInputDevice pointer_only(GraphicsWindow *host, const string &name);
-  static GraphicsWindowInputDevice keyboard_only(GraphicsWindow *host, const string &name);
-  static GraphicsWindowInputDevice pointer_and_keyboard(GraphicsWindow *host, const string &name);
+  static PT(GraphicsWindowInputDevice) pointer_only(GraphicsWindow *host, const string &name);
+  static PT(GraphicsWindowInputDevice) keyboard_only(GraphicsWindow *host, const string &name);
+  static PT(GraphicsWindowInputDevice) pointer_and_keyboard(GraphicsWindow *host, const string &name);
 
   INLINE GraphicsWindowInputDevice();
   GraphicsWindowInputDevice(const GraphicsWindowInputDevice &copy);
   void operator = (const GraphicsWindowInputDevice &copy);
   ~GraphicsWindowInputDevice();
 
-  INLINE string get_name() const;
-  INLINE bool has_pointer() const;
-  INLINE bool has_keyboard() const;
-
   INLINE void set_device_index(int index);
 
-  INLINE MouseData get_pointer() const;
-  INLINE MouseData get_raw_pointer() const;
-
-  INLINE void enable_pointer_events();
-  INLINE void disable_pointer_events();
-
-  void enable_pointer_mode(double speed);
-  void disable_pointer_mode();
-
-  bool has_button_event() const;
-  ButtonEvent get_button_event();
-  bool has_pointer_event() const;
-  PT(PointerEventList) get_pointer_events();
-
 PUBLISHED:
   // The following interface is for the various kinds of
   // GraphicsWindows to record the data incoming on the device.
-  INLINE void button_down(ButtonHandle button);
-  INLINE void button_resume_down(ButtonHandle button);
-  INLINE void button_up(ButtonHandle button);
-  INLINE void keystroke(int keycode);
-  INLINE void focus_lost();
-  INLINE void raw_button_down(ButtonHandle button);
-  INLINE void raw_button_up(ButtonHandle button);
-  INLINE void set_pointer_in_window(double x, double y);
-  INLINE void set_pointer_out_of_window();
-
-  void button_down(ButtonHandle button, double time);
-  void button_resume_down(ButtonHandle button, double time);
-  void button_up(ButtonHandle button, double time);
-  void keystroke(int keycode, double time);
+  void button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+  void button_resume_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+  void button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+
+  void keystroke(int keycode, double time = ClockObject::get_global_clock()->get_frame_time());
   void candidate(const wstring &candidate_string, size_t highlight_start,
                  size_t highlight_end, size_t cursor_pos);
-  void focus_lost(double time);
-  void raw_button_down(ButtonHandle button, double time);
-  void raw_button_up(ButtonHandle button, double time);
-
-  INLINE void set_pointer_in_window(double x, double y, double time);
-  INLINE void set_pointer_out_of_window(double time);
-  void set_pointer(bool inwin, double x, double y, double time);
 
-public:
-  // We need these methods to make VC++ happy when we try to
-  // instantiate a pvector<GraphicsWindowInputDevice>.  They don't do
-  // anything useful.
-  INLINE bool operator == (const GraphicsWindowInputDevice &other) const;
-  INLINE bool operator != (const GraphicsWindowInputDevice &other) const;
-  INLINE bool operator < (const GraphicsWindowInputDevice &other) const;
+  void focus_lost(double time = ClockObject::get_global_clock()->get_frame_time());
 
-private:
-  enum InputDeviceFlags {
-    IDF_has_pointer    = 0x01,
-    IDF_has_keyboard   = 0x02
-  };
-  typedef pdeque<ButtonEvent> ButtonEvents;
+  void raw_button_down(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
+  void raw_button_up(ButtonHandle button, double time = ClockObject::get_global_clock()->get_frame_time());
 
-  LightMutex _lock;
+  INLINE void set_pointer_in_window(double x, double y, double time = ClockObject::get_global_clock()->get_frame_time());
+  INLINE void set_pointer_out_of_window(double time = ClockObject::get_global_clock()->get_frame_time());
 
+private:
   GraphicsWindow *_host;
-
-  string _name;
-  int _flags;
   int _device_index;
-  int _event_sequence;
-
-  bool   _pointer_mode_enable;
-  double _pointer_speed;
-
-  bool _enable_pointer_events;
-  MouseData _mouse_data;
-  MouseData _true_mouse_data;
-  ButtonEvents _button_events;
-  PT(PointerEventList) _pointer_events;
 
   typedef pset<ButtonHandle> ButtonsHeld;
   ButtonsHeld _buttons_held;
-};
-
-#include "graphicsWindowInputDevice.I"
 
-#define EXPCL EXPCL_PANDA_DISPLAY
-#define EXPTP EXPTP_PANDA_DISPLAY
-#define TYPE GraphicsWindowInputDevice
-#define NAME vector_GraphicsWindowInputDevice
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    InputDevice::init_type();
+    register_type(_type_handle, "GraphicsWindowInputDevice",
+                  InputDevice::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
 
-#include "vector_src.h"
+private:
+  static TypeHandle _type_handle;
+};
 
-// Tell GCC that we'll take care of the instantiation explicitly here.
-#ifdef __GNUC__
-#pragma interface
-#endif
+#include "graphicsWindowInputDevice.I"
 
 #endif

+ 13 - 16
panda/src/device/mouseAndKeyboard.cxx → panda/src/display/mouseAndKeyboard.cxx

@@ -41,7 +41,6 @@ MouseAndKeyboard(GraphicsWindow *window, int device, const string &name) :
   _pixel_xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _pixel_size = new EventStoreVec2(LPoint2(0.0f, 0.0f));
   _xy = new EventStoreVec2(LPoint2(0.0f, 0.0f));
-  _button_events = new ButtonEventList;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -93,20 +92,18 @@ get_source_device() const {
 void MouseAndKeyboard::
 do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
                  DataNodeTransmit &output) {
-  if (_window->has_button_event(_device)) {
-    // Fill up the button events.
-    _button_events->clear();
-    while (_window->has_button_event(_device)) {
-      ButtonEvent be = _window->get_button_event(_device);
-      _button_events->add_event(be);
-    }
-    output.set_data(_button_events_output, EventParameter(_button_events));
+
+  PT(InputDevice) device = _window->get_input_device(_device);
+
+  if (device->has_button_event()) {
+    PT(ButtonEventList) bel = device->get_button_events();
+    output.set_data(_button_events_output, EventParameter(bel));
   }
-  if (_window->has_pointer_event(_device)) {
-    PT(PointerEventList) pel = _window->get_pointer_events(_device);
+  if (device->has_pointer_event()) {
+    PT(PointerEventList) pel = device->get_pointer_events();
     output.set_data(_pointer_events_output, EventParameter(pel));
   }
-  
+
   // Get the window size.
   WindowProperties properties = _window->get_properties();
   if (properties.has_size()) {
@@ -116,18 +113,18 @@ do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
     _pixel_size->set_value(LPoint2(w, h));
     output.set_data(_pixel_size_output, EventParameter(_pixel_size));
 
-    if (_window->has_pointer(_device)) {
-      const MouseData &mdata = _window->get_pointer(_device);
+    if (device->has_pointer()) {
+      const MouseData &mdata = device->get_pointer();
 
       if (mdata._in_window) {
         // Get mouse motion in pixels.
         _pixel_xy->set_value(LPoint2(mdata._xpos, mdata._ypos));
         output.set_data(_pixel_xy_output, EventParameter(_pixel_xy));
-        
+
         // Normalize pixel motion to range [-1,1].
         PN_stdfloat xf = (PN_stdfloat)(2 * mdata._xpos) / (PN_stdfloat)w - 1.0f;
         PN_stdfloat yf = 1.0f - (PN_stdfloat)(2 * mdata._ypos) / (PN_stdfloat)h;
-        
+
         _xy->set_value(LPoint2(xf, yf));
         output.set_data(_xy_output, EventParameter(_xy));
       }

+ 1 - 2
panda/src/device/mouseAndKeyboard.h → panda/src/display/mouseAndKeyboard.h

@@ -51,7 +51,7 @@ PUBLISHED:
 
   PT(GraphicsWindow) get_source_window() const;
   int                get_source_device() const;
-  
+
 protected:
   // Inherited from DataNode
   virtual void do_transmit_data(DataGraphTraverser *trav,
@@ -69,7 +69,6 @@ private:
   PT(EventStoreVec2) _pixel_xy;
   PT(EventStoreVec2) _pixel_size;
   PT(EventStoreVec2) _xy;
-  PT(ButtonEventList) _button_events;
 
   PT(GraphicsWindow) _window;
   int _device;

+ 1 - 0
panda/src/display/p3display_composite2.cxx

@@ -5,6 +5,7 @@
 #include "graphicsWindowProc.cxx"
 #include "graphicsWindowProcCallbackData.cxx"
 #include "graphicsWindowInputDevice.cxx"
+#include "mouseAndKeyboard.cxx"
 #include "nativeWindowHandle.cxx"
 #include "parasiteBuffer.cxx"
 #include "standardMunger.cxx"

+ 2 - 0
panda/src/putil/config_util.cxx

@@ -32,6 +32,7 @@
 #include "datagram.h"
 #include "doubleBitMask.h"
 #include "factoryParam.h"
+#include "gamepadButton.h"
 #include "namable.h"
 #include "nodeCachedReferenceCount.h"
 #include "paramValue.h"
@@ -225,6 +226,7 @@ init_libputil() {
   WritableConfigurable::init_type();
   WritableParam::init_type();
 
+  GamepadButton::init_gamepad_buttons();
   KeyboardButton::init_keyboard_buttons();
   MouseButton::init_mouse_buttons();
 

+ 75 - 0
panda/src/putil/gamepadButton.cxx

@@ -0,0 +1,75 @@
+// Filename: gamepadButton.cxx
+// Created by:  rdb (21Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "gamepadButton.h"
+#include "buttonRegistry.h"
+
+#define DEFINE_GAMEPAD_BUTTON_HANDLE(KeyName)     \
+                  static ButtonHandle _##KeyName; \
+                  ButtonHandle GamepadButton::KeyName() { return _##KeyName; }
+
+DEFINE_GAMEPAD_BUTTON_HANDLE(lstick)
+DEFINE_GAMEPAD_BUTTON_HANDLE(rstick)
+DEFINE_GAMEPAD_BUTTON_HANDLE(lshoulder)
+DEFINE_GAMEPAD_BUTTON_HANDLE(rshoulder)
+DEFINE_GAMEPAD_BUTTON_HANDLE(ltrigger)
+DEFINE_GAMEPAD_BUTTON_HANDLE(rtrigger)
+
+DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_left)
+DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_right)
+DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_up)
+DEFINE_GAMEPAD_BUTTON_HANDLE(dpad_down)
+
+DEFINE_GAMEPAD_BUTTON_HANDLE(back)
+DEFINE_GAMEPAD_BUTTON_HANDLE(guide)
+DEFINE_GAMEPAD_BUTTON_HANDLE(start)
+
+DEFINE_GAMEPAD_BUTTON_HANDLE(action_a)
+DEFINE_GAMEPAD_BUTTON_HANDLE(action_b)
+DEFINE_GAMEPAD_BUTTON_HANDLE(action_c)
+DEFINE_GAMEPAD_BUTTON_HANDLE(action_x)
+DEFINE_GAMEPAD_BUTTON_HANDLE(action_y)
+DEFINE_GAMEPAD_BUTTON_HANDLE(action_z)
+
+////////////////////////////////////////////////////////////////////
+//     Function: GamepadButton::init_gamepad_buttons
+//       Access: Public, Static
+//  Description: This is intended to be called only once, by the
+//               static initialization performed in config_util.cxx.
+////////////////////////////////////////////////////////////////////
+void GamepadButton::
+init_gamepad_buttons() {
+  ButtonRegistry::ptr()->register_button(_lstick, "lstick");
+  ButtonRegistry::ptr()->register_button(_rstick, "rstick");
+  ButtonRegistry::ptr()->register_button(_lshoulder, "lshoulder");
+  ButtonRegistry::ptr()->register_button(_rshoulder, "rshoulder");
+  ButtonRegistry::ptr()->register_button(_ltrigger, "ltrigger");
+  ButtonRegistry::ptr()->register_button(_rtrigger, "rtrigger");
+
+  ButtonRegistry::ptr()->register_button(_dpad_left, "dpad_left");
+  ButtonRegistry::ptr()->register_button(_dpad_right, "dpad_right");
+  ButtonRegistry::ptr()->register_button(_dpad_up, "dpad_up");
+  ButtonRegistry::ptr()->register_button(_dpad_down, "dpad_down");
+
+  ButtonRegistry::ptr()->register_button(_back, "back");
+  ButtonRegistry::ptr()->register_button(_guide, "guide");
+  ButtonRegistry::ptr()->register_button(_start, "start");
+
+  ButtonRegistry::ptr()->register_button(_action_a, "action_a");
+  ButtonRegistry::ptr()->register_button(_action_b, "action_b");
+  ButtonRegistry::ptr()->register_button(_action_c, "action_c");
+  ButtonRegistry::ptr()->register_button(_action_x, "action_x");
+  ButtonRegistry::ptr()->register_button(_action_y, "action_y");
+  ButtonRegistry::ptr()->register_button(_action_z, "action_z");
+}

+ 57 - 0
panda/src/putil/gamepadButton.h

@@ -0,0 +1,57 @@
+// Filename: gamepadButton.h
+// Created by:  rdb (21Aug15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef GAMEPADBUTTON_H
+#define GAMEPADBUTTON_H
+
+#include "pandabase.h"
+
+#include "buttonHandle.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : GamepadButton
+// Description : This class is just used as a convenient namespace for
+//               grouping all of these handy functions that return
+//               buttons which map to gamepad buttons.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_PUTIL GamepadButton {
+PUBLISHED:
+  static ButtonHandle lstick();
+  static ButtonHandle rstick();
+  static ButtonHandle lshoulder();
+  static ButtonHandle rshoulder();
+  static ButtonHandle ltrigger();
+  static ButtonHandle rtrigger();
+
+  static ButtonHandle dpad_left();
+  static ButtonHandle dpad_right();
+  static ButtonHandle dpad_up();
+  static ButtonHandle dpad_down();
+
+  static ButtonHandle back();
+  static ButtonHandle guide();
+  static ButtonHandle start();
+
+  static ButtonHandle action_a();
+  static ButtonHandle action_b();
+  static ButtonHandle action_c();
+  static ButtonHandle action_x();
+  static ButtonHandle action_y();
+  static ButtonHandle action_z();
+
+public:
+  static void init_gamepad_buttons();
+};
+
+#endif

+ 0 - 1
panda/src/putil/p3putil_composite1.cxx

@@ -28,4 +28,3 @@
 #include "factoryBase.cxx"
 #include "factoryParam.cxx"
 #include "factoryParams.cxx"
-#include "globalPointerRegistry.cxx"

+ 2 - 0
panda/src/putil/p3putil_composite2.cxx

@@ -1,3 +1,5 @@
+#include "gamepadButton.cxx"
+#include "globalPointerRegistry.cxx"
 #include "ioPtaDatagramFloat.cxx"
 #include "ioPtaDatagramInt.cxx"
 #include "ioPtaDatagramShort.cxx"

+ 16 - 16
panda/src/tinydisplay/tinyOsxGraphicsWindow.mm

@@ -419,7 +419,7 @@ OSStatus TinyOsxGraphicsWindow::handleTextInput (EventHandlerCallRef myHandler,
     }
 
     for (unsigned int x = 0; x < actualSize/sizeof(UniChar); ++x) {
-      _input_devices[0].keystroke(text[x]);
+      _input_devices[0]->keystroke(text[x]);
     }
     DisposePtr((char *)text);
   }
@@ -492,10 +492,10 @@ TinyOsxGraphicsWindow::TinyOsxGraphicsWindow(GraphicsEngine *engine, GraphicsPip
   _current_icon(NULL),
   _ID(id_seed++),
   _originalMode(NULL) {
- GraphicsWindowInputDevice device =
+  PT(InputDevice) device =
     GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse");
   _input_devices.push_back(device);
-  _input_devices[0].set_pointer_in_window(0, 0);
+  device->set_pointer_in_window(0, 0);
   _last_key_modifiers = 0;
   _last_buttons = 0;
 
@@ -579,7 +579,7 @@ bool TinyOsxGraphicsWindow::set_icon_filename(const Filename &icon_filename) {
 ////////////////////////////////////////////////////////////////////
 void TinyOsxGraphicsWindow::
 set_pointer_in_window(int x, int y) {
-  _input_devices[0].set_pointer_in_window(x, y);
+  _input_devices[0]->set_pointer_in_window(x, y);
 
   if (_cursor_hidden != _display_hide_cursor) {
     if (_cursor_hidden) {
@@ -600,7 +600,7 @@ set_pointer_in_window(int x, int y) {
 ////////////////////////////////////////////////////////////////////
 void TinyOsxGraphicsWindow::
 set_pointer_out_of_window() {
-  _input_devices[0].set_pointer_out_of_window();
+  _input_devices[0]->set_pointer_out_of_window();
 
   if (_display_hide_cursor) {
     CGDisplayShowCursor(kCGDirectMainDisplay);
@@ -1333,13 +1333,13 @@ void TinyOsxGraphicsWindow::SystemPointToLocalPoint(Point &qdGlobalPoint) {
 	_wheel_delta += this_wheel_delta;
 	SInt32 wheel_scale = osx_mouse_wheel_scale;
 	while (_wheel_delta > wheel_scale) {
-	  _input_devices[0].button_down(MouseButton::wheel_up());
-	  _input_devices[0].button_up(MouseButton::wheel_up());
+	  _input_devices[0]->button_down(MouseButton::wheel_up());
+	  _input_devices[0]->button_up(MouseButton::wheel_up());
 	  _wheel_delta -= wheel_scale;
 	}
 	while (_wheel_delta < -wheel_scale) {
-	  _input_devices[0].button_down(MouseButton::wheel_down());
-	  _input_devices[0].button_up(MouseButton::wheel_down());
+	  _input_devices[0]->button_down(MouseButton::wheel_down());
+	  _input_devices[0]->button_up(MouseButton::wheel_down());
 	  _wheel_delta += wheel_scale;
 	}
       }
@@ -1519,25 +1519,25 @@ HandleButtonDelta(UInt32 new_buttons) {
 
   if (changed & 0x01) {
     if (new_buttons & 0x01) {
-      _input_devices[0].button_down(MouseButton::one());
+      _input_devices[0]->button_down(MouseButton::one());
     } else {
-      _input_devices[0].button_up(MouseButton::one());
+      _input_devices[0]->button_up(MouseButton::one());
     }
   }
 
   if (changed & 0x04) {
     if (new_buttons & 0x04) {
-      _input_devices[0].button_down(MouseButton::two());
+      _input_devices[0]->button_down(MouseButton::two());
     } else {
-      _input_devices[0].button_up(MouseButton::two());
+      _input_devices[0]->button_up(MouseButton::two());
     }
   }
 
   if (changed & 0x02) {
     if (new_buttons & 0x02) {
-      _input_devices[0].button_down(MouseButton::three());
+      _input_devices[0]->button_down(MouseButton::three());
     } else {
-      _input_devices[0].button_up(MouseButton::three());
+      _input_devices[0]->button_up(MouseButton::three());
     }
   }
 
@@ -1745,7 +1745,7 @@ void TinyOsxGraphicsWindow::set_properties_now(WindowProperties &properties) {
   if (properties.has_cursor_hidden()) {
     _properties.set_cursor_hidden(properties.get_cursor_hidden());
     _cursor_hidden = properties.get_cursor_hidden();
-    if (_cursor_hidden && _input_devices[0].has_pointer()) {
+    if (_cursor_hidden && _input_devices[0]->has_pointer()) {
       if (!_display_hide_cursor) {
         CGDisplayHideCursor(kCGDirectMainDisplay);
         _display_hide_cursor = true;

+ 9 - 11
panda/src/tinydisplay/tinySDLGraphicsWindow.cxx

@@ -46,9 +46,7 @@ TinySDLGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   _pitch = 0;
   update_pixel_factor();
 
-  GraphicsWindowInputDevice device =
-    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
-  add_input_device(device);
+  add_input_device(GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -184,35 +182,35 @@ process_events() {
     switch(evt.type) {
     case SDL_KEYDOWN:
       if (evt.key.keysym.unicode) {
-        _input_devices[0].keystroke(evt.key.keysym.unicode);
+        _input_devices[0]->keystroke(evt.key.keysym.unicode);
       }
       button = get_keyboard_button(evt.key.keysym.sym);
       if (button != ButtonHandle::none()) {
-        _input_devices[0].button_down(button);
+        _input_devices[0]->button_down(button);
       }
       break;
 
     case SDL_KEYUP:
       button = get_keyboard_button(evt.key.keysym.sym);
       if (button != ButtonHandle::none()) {
-        _input_devices[0].button_up(button);
+        _input_devices[0]->button_up(button);
       }
       break;
 
     case SDL_MOUSEBUTTONDOWN:
       button = get_mouse_button(evt.button.button);
-      _input_devices[0].set_pointer_in_window(evt.button.x, evt.button.y);
-      _input_devices[0].button_down(button);
+      _input_devices[0]->set_pointer_in_window(evt.button.x, evt.button.y);
+      _input_devices[0]->button_down(button);
       break;
 
     case SDL_MOUSEBUTTONUP:
       button = get_mouse_button(evt.button.button);
-      _input_devices[0].set_pointer_in_window(evt.button.x, evt.button.y);
-      _input_devices[0].button_up(button);
+      _input_devices[0]->set_pointer_in_window(evt.button.x, evt.button.y);
+      _input_devices[0]->button_up(button);
       break;
 
     case SDL_MOUSEMOTION:
-      _input_devices[0].set_pointer_in_window(evt.motion.x, evt.motion.y);
+      _input_devices[0]->set_pointer_in_window(evt.motion.x, evt.motion.y);
       break;
      
     case SDL_VIDEORESIZE:

+ 9 - 11
panda/src/tinydisplay/tinyXGraphicsWindow.cxx

@@ -215,9 +215,7 @@ process_events() {
   if (_xwindow == (X11_Window)0) {
     return;
   }
-  
-  poll_raw_mice();
-  
+
   XEvent event;
   XKeyEvent keyrelease_event;
   bool got_keyrelease_event = false;
@@ -294,18 +292,18 @@ process_events() {
     case ButtonPress:
       // This refers to the mouse buttons.
       button = get_mouse_button(event.xbutton);
-      _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
-      _input_devices[0].button_down(button);
+      _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+      _input->button_down(button);
       break;
       
     case ButtonRelease:
       button = get_mouse_button(event.xbutton);
-      _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
-      _input_devices[0].button_up(button);
+      _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+      _input->button_up(button);
       break;
 
     case MotionNotify:
-      _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
+      _input->set_pointer_in_window(event.xmotion.x, event.xmotion.y);
       break;
 
     case KeyPress:
@@ -322,11 +320,11 @@ process_events() {
       break;
 
     case EnterNotify:
-      _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
+      _input->set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
       break;
 
     case LeaveNotify:
-      _input_devices[0].set_pointer_out_of_window();
+      _input->set_pointer_out_of_window();
       break;
 
     case FocusIn:
@@ -335,7 +333,7 @@ process_events() {
       break;
 
     case FocusOut:
-      _input_devices[0].focus_lost();
+      _input->focus_lost();
       properties.set_foreground(false);
       system_changed_properties(properties);
       break;

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

@@ -115,7 +115,6 @@ vrpn_analog_callback(void *userdata, const vrpn_ANALOGCB info) {
   Devices::iterator di;
   for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
     VrpnAnalogDevice *device = (*di);
-    device->acquire();
     for (int i = 0; i < info.num_channel; i++) {
       if (vrpn_cat.is_debug()) {
         if (device->get_control_state(i) != info.channel[i]) {
@@ -125,6 +124,5 @@ vrpn_analog_callback(void *userdata, const vrpn_ANALOGCB info) {
       }
       device->set_control_state(i, info.channel[i]);
     }
-    device->unlock();
   }
 }

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

@@ -119,8 +119,6 @@ vrpn_button_callback(void *userdata, const vrpn_BUTTONCB info) {
   Devices::iterator di;
   for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
     VrpnButtonDevice *device = (*di);
-    device->acquire();
     device->set_button_state(info.button, info.state != 0);
-    device->unlock();
   }
 }

+ 0 - 2
panda/src/vrpn/vrpnDial.cxx

@@ -120,8 +120,6 @@ vrpn_dial_callback(void *userdata, const vrpn_DIALCB info) {
   Devices::iterator di;
   for (di = self->_devices.begin(); di != self->_devices.end(); ++di) {
     VrpnDialDevice *device = (*di);
-    device->acquire();
     device->push_dial(info.dial, info.change);
-    device->unlock();
   }
 }

+ 12 - 19
panda/src/vrpn/vrpnTracker.cxx

@@ -123,11 +123,10 @@ vrpn_position_callback(void *userdata, const vrpn_TRACKERCB info) {
     VrpnTrackerDevice *device = (*di);
     if (device->get_sensor() == info.sensor &&
         device->get_data_type() == VrpnTrackerDevice::DT_position) {
-      device->acquire();
-      device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time));
-      device->_data.set_pos(LPoint3(info.pos[0], info.pos[1], info.pos[2]));
-      device->_data.set_orient(LOrientation(info.quat[3], info.quat[0], info.quat[1], info.quat[2]));
-      device->unlock();
+      device->set_tracker(LPoint3(info.pos[0], info.pos[1], info.pos[2]),
+                          LOrientation(info.quat[3], info.quat[0],
+                                       info.quat[1], info.quat[2]),
+                          VrpnClient::convert_to_secs(info.msg_time));
     }
   }
 }
@@ -152,13 +151,10 @@ vrpn_velocity_callback(void *userdata, const vrpn_TRACKERVELCB info) {
     VrpnTrackerDevice *device = (*di);
     if (device->get_sensor() == info.sensor &&
         device->get_data_type() == VrpnTrackerDevice::DT_velocity) {
-      device->acquire();
-      device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time));
-      device->_data.set_pos(LPoint3(info.vel[0], info.vel[1], info.vel[2]));
-      device->_data.set_orient(LOrientation(info.vel_quat[3], info.vel_quat[0],
-                                             info.vel_quat[1], info.vel_quat[2]));
-      device->_data.set_dt(info.vel_quat_dt);
-      device->unlock();
+      device->set_tracker(LPoint3(info.vel[0], info.vel[1], info.vel[2]),
+                          LOrientation(info.vel_quat[3], info.vel_quat[0],
+                                       info.vel_quat[1], info.vel_quat[2]),
+                          VrpnClient::convert_to_secs(info.msg_time));
     }
   }
 }
@@ -183,13 +179,10 @@ vrpn_acceleration_callback(void *userdata, const vrpn_TRACKERACCCB info) {
     VrpnTrackerDevice *device = (*di);
     if (device->get_sensor() == info.sensor &&
         device->get_data_type() == VrpnTrackerDevice::DT_acceleration) {
-      device->acquire();
-      device->_data.set_time(VrpnClient::convert_to_secs(info.msg_time));
-      device->_data.set_pos(LPoint3(info.acc[0], info.acc[1], info.acc[2]));
-      device->_data.set_orient(LOrientation(info.acc_quat[3], info.acc_quat[0],
-                                             info.acc_quat[1], info.acc_quat[2]));
-      device->_data.set_dt(info.acc_quat_dt);
-      device->unlock();
+      device->set_tracker(LPoint3(info.acc[0], info.acc[1], info.acc[2]),
+                          LOrientation(info.acc_quat[3], info.acc_quat[0],
+                                       info.acc_quat[1], info.acc_quat[2]),
+                          VrpnClient::convert_to_secs(info.msg_time));
     }
   }
 }

+ 3 - 3
panda/src/windisplay/winGraphicsWindow.cxx

@@ -2786,9 +2786,9 @@ handle_raw_input(HRAWINPUT hraw) {
       if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
         _input_devices[i].set_pointer_in_window(adjx, adjy);
       } else {
-        int oldx = _input_devices[i].get_raw_pointer().get_x();
-        int oldy = _input_devices[i].get_raw_pointer().get_y();
-        _input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy);
+        //int oldx = _input_devices[i].get_raw_pointer().get_x();
+        //int oldy = _input_devices[i].get_raw_pointer().get_y();
+        //_input_devices[i].set_pointer_in_window(oldx + adjx, oldy + adjy);
       }
 
       if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) {

+ 78 - 269
panda/src/x11display/x11GraphicsWindow.cxx

@@ -28,14 +28,10 @@
 #include "nativeWindowHandle.h"
 #include "virtualFileSystem.h"
 #include "get_x11.h"
+#include "evdevInputDevice.h"
 
-#include <errno.h>
-#include <fcntl.h>
 #include <sys/time.h>
-
-#ifdef PHAVE_LINUX_INPUT_H
-#include <linux/input.h>
-#endif
+#include <fcntl.h>
 
 #ifdef HAVE_XCURSOR
 static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) {
@@ -69,8 +65,6 @@ static int xcursor_seek(XcursorFile *file, long offset, int whence) {
 
 TypeHandle x11GraphicsWindow::_type_handle;
 
-#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
-
 ////////////////////////////////////////////////////////////////////
 //     Function: x11GraphicsWindow::Constructor
 //       Access: Public
@@ -107,9 +101,9 @@ x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   _override_redirect = False;
   _wm_delete_window = x11_pipe->_wm_delete_window;
 
-  GraphicsWindowInputDevice device =
-    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
+  PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
   add_input_device(device);
+  _input = device;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -143,26 +137,26 @@ move_pointer(int device, int x, int y) {
   if (device == 0) {
     // Move the system mouse pointer.
     if (!_properties.get_foreground() ||
-        !_input_devices[0].get_pointer().get_in_window()) {
+        !_input->get_pointer().get_in_window()) {
       // If the window doesn't have input focus, or the mouse isn't
       // currently within the window, forget it.
       return false;
     }
 
-    const MouseData &md = _input_devices[0].get_pointer();
+    const MouseData &md = _input->get_pointer();
     if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
       if (!_dga_mouse_enabled) {
         XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
       }
-      _input_devices[0].set_pointer_in_window(x, y);
+      _input->set_pointer_in_window(x, y);
     }
     return true;
   } else {
     // Move a raw mouse.
-    if ((device < 1)||(device >= _input_devices.size())) {
+    if (device < 1 || device >= _input_devices.size()) {
       return false;
     }
-    _input_devices[device].set_pointer_in_window(x, y);
+    //_input_devices[device]->set_pointer_in_window(x, y);
     return true;
   }
 }
@@ -249,8 +243,6 @@ process_events() {
     return;
   }
 
-  poll_raw_mice();
-
   XEvent event;
   XKeyEvent keyrelease_event;
   bool got_keyrelease_event = false;
@@ -311,25 +303,25 @@ process_events() {
       // This refers to the mouse buttons.
       button = get_mouse_button(event.xbutton);
       if (!_dga_mouse_enabled) {
-        _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+        _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
       }
-      _input_devices[0].button_down(button);
+      _input->button_down(button);
       break;
 
     case ButtonRelease:
       button = get_mouse_button(event.xbutton);
       if (!_dga_mouse_enabled) {
-        _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+        _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
       }
-      _input_devices[0].button_up(button);
+      _input->button_up(button);
       break;
 
     case MotionNotify:
       if (_dga_mouse_enabled) {
-        const MouseData &md = _input_devices[0].get_raw_pointer();
-        _input_devices[0].set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root);
+        const MouseData &md = _input->get_pointer();
+        _input->set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root);
       } else {
-        _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
+        _input->set_pointer_in_window(event.xmotion.x, event.xmotion.y);
       }
       break;
 
@@ -348,15 +340,15 @@ process_events() {
 
     case EnterNotify:
       if (_dga_mouse_enabled) {
-        const MouseData &md = _input_devices[0].get_raw_pointer();
-        _input_devices[0].set_pointer_in_window(md.get_x(), md.get_y());
+        const MouseData &md = _input->get_pointer();
+        _input->set_pointer_in_window(md.get_x(), md.get_y());
       } else {
-        _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
+        _input->set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
       }
       break;
 
     case LeaveNotify:
-      _input_devices[0].set_pointer_out_of_window();
+      _input->set_pointer_out_of_window();
       break;
 
     case FocusIn:
@@ -365,7 +357,7 @@ process_events() {
       break;
 
     case FocusOut:
-      _input_devices[0].focus_lost();
+      _input->focus_lost();
       properties.set_foreground(false);
       changed_properties = true;
       break;
@@ -751,7 +743,7 @@ set_properties_now(WindowProperties &properties) {
             XQueryPointer(_display, _xwindow, &event.xbutton.root,
               &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
               &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
-            _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
+            _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
           }
         } else {
           x11display_cat.info() << "XF86DGA extension not available\n";
@@ -1295,66 +1287,43 @@ open_raw_mice() {
   bool any_mice = false;
 
   for (int i=0; i<64; i++) {
-    uint8_t evtypes[EV_MAX/8 + 1];
     ostringstream fnb;
     fnb << "/dev/input/event" << i;
     string fn = fnb.str();
     int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
     if (fd >= 0) {
-      any_present = true;
-      char name[256];
-      char phys[256];
-      char uniq[256];
-      if ((ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0)||
-          (ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0)||
-          (ioctl(fd, EVIOCGPHYS(sizeof(uniq)), uniq) < 0)||
-          (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
-        close(fd);
-        x11display_cat.error() <<
-          "Opening raw mice: ioctl failed on " << fn << "\n";
-      } else {
-        if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
-          for (char *p=name; *p; p++) {
-            if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
-              *p = '_';
-            }
-          }
-          for (char *p=uniq; *p; p++) {
-            if (((*p<'a')||(*p>'z')) && ((*p<'A')||(*p>'Z')) && ((*p<'0')||(*p>'9'))) {
-              *p = '_';
-            }
-          }
-          string full_id = ((string)name) + "." + uniq;
-          MouseDeviceInfo inf;
-          inf._fd = fd;
-          inf._input_device_index = _input_devices.size();
-          inf._io_buffer = "";
-          _mouse_device_info.push_back(inf);
-          GraphicsWindowInputDevice device =
-            GraphicsWindowInputDevice::pointer_only(this, full_id);
-          add_input_device(device);
-          x11display_cat.info() << "Raw mouse " <<
-            inf._input_device_index << " detected: " << full_id << "\n";
-          any_mice = true;
-        } else {
-          close(fd);
-        }
+      EvdevInputDevice *device = new EvdevInputDevice(fd);
+      nassertd(device != NULL) continue;
+
+      if (device->has_pointer()) {
+        add_input_device(device);
+
+        x11display_cat.info()
+          << "Raw mouse " << _input_devices.size()
+          << " detected: " << device->get_name() << "\n";
+
+        any_mice = true;
+        any_present = true;
       }
     } else {
-      if ((errno == ENOENT)||(errno == ENOTDIR)) {
+      if (errno == ENOENT || errno == ENOTDIR) {
         break;
       } else {
         any_present = true;
-        x11display_cat.error() <<
-          "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
+        x11display_cat.error()
+          << "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
       }
     }
   }
 
-  if (!any_present) {
+  if (any_mice) {
+    _properties.set_raw_mice(true);
+
+  } else if (!any_present) {
     x11display_cat.error() <<
       "Opening raw mice: files not found: /dev/input/event*\n";
-  } else if (!any_mice) {
+
+  } else {
     x11display_cat.error() <<
       "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
   }
@@ -1364,69 +1333,6 @@ open_raw_mice() {
 #endif
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: x11GraphicsWindow::poll_raw_mice
-//       Access: Private
-//  Description: Reads events from the raw mouse device files.
-////////////////////////////////////////////////////////////////////
-void x11GraphicsWindow::
-poll_raw_mice() {
-#ifdef PHAVE_LINUX_INPUT_H
-  for (int di = 0; di < _mouse_device_info.size(); ++di) {
-    MouseDeviceInfo &inf = _mouse_device_info[di];
-
-    // Read all bytes into buffer.
-    if (inf._fd >= 0) {
-      while (1) {
-        char tbuf[1024];
-        int nread = read(inf._fd, tbuf, sizeof(tbuf));
-        if (nread > 0) {
-          inf._io_buffer += string(tbuf, nread);
-        } else {
-          if ((nread < 0) && ((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
-            break;
-          }
-          close(inf._fd);
-          inf._fd = -1;
-          break;
-        }
-      }
-    }
-
-    // Process events.
-    int nevents = inf._io_buffer.size() / sizeof(struct input_event);
-    if (nevents == 0) {
-      continue;
-    }
-    const input_event *events = (const input_event *)(inf._io_buffer.c_str());
-    GraphicsWindowInputDevice &dev = _input_devices[inf._input_device_index];
-    int x = dev.get_raw_pointer().get_x();
-    int y = dev.get_raw_pointer().get_y();
-    for (int i = 0; i < nevents; i++) {
-      if (events[i].type == EV_REL) {
-        if (events[i].code == REL_X) x += events[i].value;
-        if (events[i].code == REL_Y) y += events[i].value;
-      } else if (events[i].type == EV_ABS) {
-        if (events[i].code == ABS_X) x = events[i].value;
-        if (events[i].code == ABS_Y) y = events[i].value;
-      } else if (events[i].type == EV_KEY) {
-        if ((events[i].code >= BTN_MOUSE) && (events[i].code < BTN_MOUSE + 8)) {
-          int btn = events[i].code - BTN_MOUSE;
-          dev.set_pointer_in_window(x, y);
-          if (events[i].value) {
-            dev.button_down(MouseButton::button(btn));
-          } else {
-            dev.button_up(MouseButton::button(btn));
-          }
-        }
-      }
-    }
-    inf._io_buffer.erase(0, nevents * sizeof(struct input_event));
-    dev.set_pointer_in_window(x, y);
-  }
-#endif
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: x11GraphicsWindow::handle_keystroke
 //       Access: Private
@@ -1436,7 +1342,7 @@ poll_raw_mice() {
 void x11GraphicsWindow::
 handle_keystroke(XKeyEvent &event) {
   if (!_dga_mouse_enabled) {
-    _input_devices[0].set_pointer_in_window(event.x, event.y);
+    _input->set_pointer_in_window(event.x, event.y);
   }
 
   if (_ic) {
@@ -1454,14 +1360,14 @@ handle_keystroke(XKeyEvent &event) {
     // Now each of the returned wide characters represents a
     // keystroke.
     for (int i = 0; i < len; i++) {
-      _input_devices[0].keystroke(buffer[i]);
+      _input->keystroke(buffer[i]);
     }
 
   } else {
     // Without an input context, just get the ascii keypress.
     ButtonHandle button = get_button(event, true);
     if (button.has_ascii_equivalent()) {
-      _input_devices[0].keystroke(button.get_ascii_equivalent());
+      _input->keystroke(button.get_ascii_equivalent());
     }
   }
 }
@@ -1475,30 +1381,32 @@ handle_keystroke(XKeyEvent &event) {
 void x11GraphicsWindow::
 handle_keypress(XKeyEvent &event) {
   if (!_dga_mouse_enabled) {
-    _input_devices[0].set_pointer_in_window(event.x, event.y);
+    _input->set_pointer_in_window(event.x, event.y);
   }
 
   // Now get the raw unshifted button.
   ButtonHandle button = get_button(event, false);
   if (button != ButtonHandle::none()) {
     if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
-      _input_devices[0].button_down(KeyboardButton::control());
+      _input->button_down(KeyboardButton::control());
     }
     if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
-      _input_devices[0].button_down(KeyboardButton::shift());
+      _input->button_down(KeyboardButton::shift());
     }
     if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
-      _input_devices[0].button_down(KeyboardButton::alt());
+      _input->button_down(KeyboardButton::alt());
     }
     if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
-      _input_devices[0].button_down(KeyboardButton::meta());
+      _input->button_down(KeyboardButton::meta());
     }
-    _input_devices[0].button_down(button);
+    _input->button_down(button);
   }
 
-  ButtonHandle raw_button = map_raw_button(event.keycode);
-  if (raw_button != ButtonHandle::none()) {
-    _input_devices[0].raw_button_down(raw_button);
+  if (event.keycode >= 9 && event.keycode <= 135) {
+    ButtonHandle raw_button = map_raw_button(event.keycode);
+    if (raw_button != ButtonHandle::none()) {
+      _input->raw_button_down(raw_button);
+    }
   }
 }
 
@@ -1511,30 +1419,32 @@ handle_keypress(XKeyEvent &event) {
 void x11GraphicsWindow::
 handle_keyrelease(XKeyEvent &event) {
   if (!_dga_mouse_enabled) {
-    _input_devices[0].set_pointer_in_window(event.x, event.y);
+    _input->set_pointer_in_window(event.x, event.y);
   }
 
   // Now get the raw unshifted button.
   ButtonHandle button = get_button(event, false);
   if (button != ButtonHandle::none()) {
     if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
-      _input_devices[0].button_up(KeyboardButton::control());
+      _input->button_up(KeyboardButton::control());
     }
     if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
-      _input_devices[0].button_up(KeyboardButton::shift());
+      _input->button_up(KeyboardButton::shift());
     }
     if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
-      _input_devices[0].button_up(KeyboardButton::alt());
+      _input->button_up(KeyboardButton::alt());
     }
     if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
-      _input_devices[0].button_up(KeyboardButton::meta());
+      _input->button_up(KeyboardButton::meta());
     }
-    _input_devices[0].button_up(button);
+    _input->button_up(button);
   }
 
-  ButtonHandle raw_button = map_raw_button(event.keycode);
-  if (raw_button != ButtonHandle::none()) {
-    _input_devices[0].raw_button_up(raw_button);
+  if (event.keycode >= 9 && event.keycode <= 135) {
+    ButtonHandle raw_button = map_raw_button(event.keycode);
+    if (raw_button != ButtonHandle::none()) {
+      _input->raw_button_up(raw_button);
+    }
   }
 }
 
@@ -1617,7 +1527,7 @@ get_button(XKeyEvent &key_event, bool allow_shift) {
     // can do this in just the ASCII set, because we handle
     // international keyboards elsewhere (via an input context).
     if ((key_event.state & (ShiftMask | LockMask)) != 0) {
-      if (key >= XK_a and key <= XK_z) {
+      if (key >= XK_a && key <= XK_z) {
         key += (XK_A - XK_a);
       }
     }
@@ -1962,117 +1872,16 @@ map_button(KeySym key) const {
 ////////////////////////////////////////////////////////////////////
 ButtonHandle x11GraphicsWindow::
 map_raw_button(KeyCode key) const {
-  switch (key) {
-  case 9:  return KeyboardButton::escape();
-  case 10: return KeyboardButton::ascii_key('1');
-  case 11: return KeyboardButton::ascii_key('2');
-  case 12: return KeyboardButton::ascii_key('3');
-  case 13: return KeyboardButton::ascii_key('4');
-  case 14: return KeyboardButton::ascii_key('5');
-  case 15: return KeyboardButton::ascii_key('6');
-  case 16: return KeyboardButton::ascii_key('7');
-  case 17: return KeyboardButton::ascii_key('8');
-  case 18: return KeyboardButton::ascii_key('9');
-  case 19: return KeyboardButton::ascii_key('0');
-  case 20: return KeyboardButton::ascii_key('-');
-  case 21: return KeyboardButton::ascii_key('=');
-  case 22: return KeyboardButton::backspace();
-  case 23: return KeyboardButton::tab();
-  case 24: return KeyboardButton::ascii_key('q');
-  case 25: return KeyboardButton::ascii_key('w');
-  case 26: return KeyboardButton::ascii_key('e');
-  case 27: return KeyboardButton::ascii_key('r');
-  case 28: return KeyboardButton::ascii_key('t');
-  case 29: return KeyboardButton::ascii_key('y');
-  case 30: return KeyboardButton::ascii_key('u');
-  case 31: return KeyboardButton::ascii_key('i');
-  case 32: return KeyboardButton::ascii_key('o');
-  case 33: return KeyboardButton::ascii_key('p');
-  case 34: return KeyboardButton::ascii_key('[');
-  case 35: return KeyboardButton::ascii_key(']');
-  case 36: return KeyboardButton::enter();
-  case 37: return KeyboardButton::lcontrol();
-  case 38: return KeyboardButton::ascii_key('a');
-  case 39: return KeyboardButton::ascii_key('s');
-  case 40: return KeyboardButton::ascii_key('d');
-  case 41: return KeyboardButton::ascii_key('f');
-  case 42: return KeyboardButton::ascii_key('g');
-  case 43: return KeyboardButton::ascii_key('h');
-  case 44: return KeyboardButton::ascii_key('j');
-  case 45: return KeyboardButton::ascii_key('k');
-  case 46: return KeyboardButton::ascii_key('l');
-  case 47: return KeyboardButton::ascii_key(';');
-  case 48: return KeyboardButton::ascii_key('\'');
-  case 49: return KeyboardButton::ascii_key('`');
-  case 50: return KeyboardButton::lshift();
-  case 51: return KeyboardButton::ascii_key('\\');
-  case 52: return KeyboardButton::ascii_key('z');
-  case 53: return KeyboardButton::ascii_key('x');
-  case 54: return KeyboardButton::ascii_key('c');
-  case 55: return KeyboardButton::ascii_key('v');
-  case 56: return KeyboardButton::ascii_key('b');
-  case 57: return KeyboardButton::ascii_key('n');
-  case 58: return KeyboardButton::ascii_key('m');
-  case 59: return KeyboardButton::ascii_key(',');
-  case 60: return KeyboardButton::ascii_key('.');
-  case 61: return KeyboardButton::ascii_key('/');
-  case 62: return KeyboardButton::rshift();
-  case 63: return KeyboardButton::ascii_key('*');
-  case 64: return KeyboardButton::lalt();
-  case 65: return KeyboardButton::space();
-  case 66: return KeyboardButton::caps_lock();
-  case 67: return KeyboardButton::f1();
-  case 68: return KeyboardButton::f2();
-  case 69: return KeyboardButton::f3();
-  case 70: return KeyboardButton::f4();
-  case 71: return KeyboardButton::f5();
-  case 72: return KeyboardButton::f6();
-  case 73: return KeyboardButton::f7();
-  case 74: return KeyboardButton::f8();
-  case 75: return KeyboardButton::f9();
-  case 76: return KeyboardButton::f10();
-  case 77: return KeyboardButton::num_lock();
-  case 78: return KeyboardButton::scroll_lock();
-  case 79: return KeyboardButton::ascii_key('7');
-  case 80: return KeyboardButton::ascii_key('8');
-  case 81: return KeyboardButton::ascii_key('9');
-  case 82: return KeyboardButton::ascii_key('-');
-  case 83: return KeyboardButton::ascii_key('4');
-  case 84: return KeyboardButton::ascii_key('5');
-  case 85: return KeyboardButton::ascii_key('6');
-  case 86: return KeyboardButton::ascii_key('+');
-  case 87: return KeyboardButton::ascii_key('1');
-  case 88: return KeyboardButton::ascii_key('2');
-  case 89: return KeyboardButton::ascii_key('3');
-  case 90: return KeyboardButton::ascii_key('0');
-  case 91: return KeyboardButton::ascii_key('.');
-
-  case 95: return KeyboardButton::f11();
-  case 96: return KeyboardButton::f12();
-
-  case 104: return KeyboardButton::enter();
-  case 105: return KeyboardButton::rcontrol();
-  case 106: return KeyboardButton::ascii_key('/');
-  case 107: return KeyboardButton::print_screen();
-  case 108: return KeyboardButton::ralt();
-
-  case 110: return KeyboardButton::home();
-  case 111: return KeyboardButton::up();
-  case 112: return KeyboardButton::page_up();
-  case 113: return KeyboardButton::left();
-  case 114: return KeyboardButton::right();
-  case 115: return KeyboardButton::end();
-  case 116: return KeyboardButton::down();
-  case 117: return KeyboardButton::page_down();
-  case 118: return KeyboardButton::insert();
-  case 119: return KeyboardButton::del();
-
-  case 127: return KeyboardButton::pause();
-
-  case 133: return KeyboardButton::lmeta();
-  case 134: return KeyboardButton::rmeta();
-  case 135: return KeyboardButton::menu();
+#ifdef PHAVE_LINUX_INPUT_H
+  // Most X11 servers are configured to use the evdev driver, which
+  // adds 8 to the underlying evdev keycodes (not sure why).
+  // In any case, this means we can use the same mapping as our raw
+  // input code, which uses evdev directly.
+  int index = key - 8;
+  if (index >= 0) {
+    return EvdevInputDevice::map_button(index);
   }
+#endif
   return ButtonHandle::none();
 }
 

+ 1 - 1
panda/src/x11display/x11GraphicsWindow.h

@@ -74,7 +74,6 @@ protected:
   static Bool check_event(X11_Display *display, XEvent *event, char *arg);
 
   void open_raw_mice();
-  void poll_raw_mice();
 
 private:
   X11_Cursor get_cursor(const Filename &filename);
@@ -89,6 +88,7 @@ protected:
   Colormap _colormap;
   XIC _ic;
   XVisualInfo *_visual_info;
+  GraphicsWindowInputDevice *_input;
 
   bool _have_xrandr;
 #ifdef HAVE_XRANDR