]> git.mxchange.org Git - flightgear.git/commitdiff
Advanced input subsystem - Step2: Split up current input subsystem
authortorsten <torsten>
Fri, 7 Aug 2009 18:56:48 +0000 (18:56 +0000)
committerTim Moore <timoore@redhat.com>
Sat, 8 Aug 2009 06:37:15 +0000 (08:37 +0200)
- encapsulate code into classes
- create separate subsystem for keyboard, mouse and joystick
- group new subsystems into subsystemgroup "input"

17 files changed:
src/Input/FGButton.cxx [new file with mode: 0644]
src/Input/FGButton.hxx [new file with mode: 0644]
src/Input/FGCommonInput.cxx [new file with mode: 0644]
src/Input/FGCommonInput.hxx [new file with mode: 0644]
src/Input/FGDeviceConfigurationMap.cxx [new file with mode: 0644]
src/Input/FGDeviceConfigurationMap.hxx [new file with mode: 0644]
src/Input/FGEventInput.cxx [new file with mode: 0644]
src/Input/FGEventInput.hxx [new file with mode: 0644]
src/Input/FGJoystickInput.cxx [new file with mode: 0644]
src/Input/FGJoystickInput.hxx [new file with mode: 0644]
src/Input/FGKeyboardInput.cxx [new file with mode: 0644]
src/Input/FGKeyboardInput.hxx [new file with mode: 0644]
src/Input/FGMouseInput.cxx [new file with mode: 0644]
src/Input/FGMouseInput.hxx [new file with mode: 0644]
src/Input/Makefile.am
src/Input/input.cxx
src/Input/input.hxx

diff --git a/src/Input/FGButton.cxx b/src/Input/FGButton.cxx
new file mode 100644 (file)
index 0000000..ee5953a
--- /dev/null
@@ -0,0 +1,75 @@
+// FGButton.cxx -- a simple button/key wrapper class
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGButton.hxx"
+
+FGButton::FGButton ()
+  : is_repeatable(false),
+    interval_sec(0),
+    last_dt(0),
+    last_state(0)
+{
+}
+
+FGButton::~FGButton ()
+{
+  // bindings is a list of SGSharedPtr<SGBindings>
+  // no cleanup required
+}
+
+
+void FGButton::init( const SGPropertyNode * node, const string name, string & module )
+{
+  if (node == 0) {
+    SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for button " << name);
+  } else {
+    is_repeatable = node->getBoolValue("repeatable", is_repeatable);
+    // Get the bindings for the button
+    read_bindings( node, bindings, KEYMOD_NONE, module );
+  }
+}
+
+void FGButton::update( int modifiers, bool pressed, int x, int y)
+{
+  if (pressed) {
+    // The press event may be repeated.
+    if (!last_state || is_repeatable) {
+      SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
+      for (unsigned int k = 0; k < bindings[modifiers].size(); k++) {
+        bindings[modifiers][k]->fire(x, y);
+      }
+    }
+  } else {
+    // The release event is never repeated.
+    if (last_state) {
+      SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" );
+      for (unsigned int k = 0; k < bindings[modifiers|KEYMOD_RELEASED].size(); k++)
+        bindings[modifiers|KEYMOD_RELEASED][k]->fire(x, y);
+    }
+  }
+          
+  last_state = pressed;
+}  
+
+
diff --git a/src/Input/FGButton.hxx b/src/Input/FGButton.hxx
new file mode 100644 (file)
index 0000000..6ab5347
--- /dev/null
@@ -0,0 +1,44 @@
+// FGButton.hxx -- a simple button/key wrapper class
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef FGBUTTON_H
+#define FGBUTTON_H
+
+#include "FGCommonInput.hxx"
+#include <Main/fg_os.hxx>
+
+class FGButton : public FGCommonInput {
+public:
+  FGButton();
+  virtual ~FGButton();
+  void init( const SGPropertyNode * node, const string name, string & module );
+  void update( int modifiers, bool pressed, int x = -1, int y = -1);
+  bool is_repeatable;
+  float interval_sec;
+  float last_dt;
+  int last_state;
+  binding_list_t bindings[KEYMOD_MAX];
+};
+
+#endif
diff --git a/src/Input/FGCommonInput.cxx b/src/Input/FGCommonInput.cxx
new file mode 100644 (file)
index 0000000..513e9a6
--- /dev/null
@@ -0,0 +1,71 @@
+// FGCommonInput.cxx -- common functions for all Input subsystems
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGCommonInput.hxx"
+#include <Main/globals.hxx>
+#include <Main/fg_os.hxx>
+
+void FGCommonInput::read_bindings (const SGPropertyNode * node, binding_list_t * binding_list, int modifiers, string & module )
+{
+  SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
+  vector<SGPropertyNode_ptr> bindings = node->getChildren("binding");
+  string nasal = "nasal";
+  for (unsigned int i = 0; i < bindings.size(); i++) {
+    const char *cmd = bindings[i]->getStringValue("command");
+    SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding " << cmd);
+    if (nasal.compare(cmd) == 0 && !module.empty())
+      bindings[i]->setStringValue("module", module.c_str());
+    binding_list[modifiers].push_back(new SGBinding(bindings[i], globals->get_props()));
+  }
+
+                                // Read nested bindings for modifiers
+  if (node->getChild("mod-up") != 0)
+    read_bindings(node->getChild("mod-up"), binding_list,
+                   modifiers|KEYMOD_RELEASED, module);
+
+  if (node->getChild("mod-shift") != 0)
+    read_bindings(node->getChild("mod-shift"), binding_list,
+                   modifiers|KEYMOD_SHIFT, module);
+
+  if (node->getChild("mod-ctrl") != 0)
+    read_bindings(node->getChild("mod-ctrl"), binding_list,
+                   modifiers|KEYMOD_CTRL, module);
+
+  if (node->getChild("mod-alt") != 0)
+    read_bindings(node->getChild("mod-alt"), binding_list,
+                   modifiers|KEYMOD_ALT, module);
+
+  if (node->getChild("mod-meta") != 0)
+    read_bindings(node->getChild("mod-meta"), binding_list,
+                   modifiers|KEYMOD_META, module);
+
+  if (node->getChild("mod-super") != 0)
+    read_bindings(node->getChild("mod-super"), binding_list,
+                   modifiers|KEYMOD_SUPER, module);
+
+  if (node->getChild("mod-hyper") != 0)
+    read_bindings(node->getChild("mod-hyper"), binding_list,
+                   modifiers|KEYMOD_HYPER, module);
+}
+
diff --git a/src/Input/FGCommonInput.hxx b/src/Input/FGCommonInput.hxx
new file mode 100644 (file)
index 0000000..7eee915
--- /dev/null
@@ -0,0 +1,51 @@
+// FGCommonInput.hxx -- common functions for all Input subsystems
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef FGCOMMONINPUT_H
+#define FGCOMMONINPUT_H
+
+#include <vector>
+#include <simgear/structure/SGBinding.hxx>
+
+#if defined( UL_WIN32 )
+#define TGT_PLATFORM   "windows"
+#elif defined ( UL_MAC_OSX )
+#define TGT_PLATFORM    "mac"
+#else
+#define TGT_PLATFORM   "unix"
+#endif
+
+class FGCommonInput {
+public:
+  typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
+
+  /*
+   read all "binding" nodes directly under the specified base node and fill the 
+   vector of SGBinding supplied in binding_list. Reads all the mod-xxx bindings and 
+   add the corresponding SGBindings.
+   */
+  static void read_bindings (const SGPropertyNode * base, binding_list_t * binding_list, int modifiers, string & module );
+};
+
+#endif
diff --git a/src/Input/FGDeviceConfigurationMap.cxx b/src/Input/FGDeviceConfigurationMap.cxx
new file mode 100644 (file)
index 0000000..e54860d
--- /dev/null
@@ -0,0 +1,77 @@
+// FGDeviceConfigurationMap.cxx -- a map to access xml device configuration
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGDeviceConfigurationMap.hxx"
+#include <simgear/props/props_io.hxx>
+#include <Main/globals.hxx>
+
+FGDeviceConfigurationMap::FGDeviceConfigurationMap( const char * relative_path, SGPropertyNode_ptr aBase, const char * aChildname ) :
+  base(aBase),
+  childname(aChildname)
+{
+  SGPath path(globals->get_fg_root());
+  path.append( relative_path );
+
+  int index = 1000;
+  scan_dir( path, &index);
+
+  vector<SGPropertyNode_ptr> childNodes = base->getChildren(childname);
+  for (int k = (int)childNodes.size() - 1; k >= 0; k--) {
+    SGPropertyNode *n = childNodes[k];
+    vector<SGPropertyNode_ptr> names = n->getChildren("name");
+    if (names.size() ) // && (n->getChildren("axis").size() || n->getChildren("button").size()))
+      for (unsigned int j = 0; j < names.size(); j++)
+        (*this)[names[j]->getStringValue()] = n;
+  }
+}
+
+FGDeviceConfigurationMap::~FGDeviceConfigurationMap()
+{
+  base->removeChildren( childname );
+}
+
+void FGDeviceConfigurationMap::scan_dir( SGPath & path, int *index)
+{
+  ulDir *dir = ulOpenDir(path.c_str());
+  if (dir) {
+    ulDirEnt* dent;
+    while ((dent = ulReadDir(dir)) != 0) {
+      if (dent->d_name[0] == '.')
+        continue;
+
+      SGPath p(path.str());
+      p.append(dent->d_name);
+      scan_dir(p, index);
+    }
+    ulCloseDir(dir);
+
+  } else if (path.extension() == "xml") {
+    SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path.str());
+    SGPropertyNode_ptr n = base->getChild(childname, (*index)++, true);
+    readProperties(path.str(), n);
+    n->setStringValue("source", path.c_str());
+  }
+}
+
+
diff --git a/src/Input/FGDeviceConfigurationMap.hxx b/src/Input/FGDeviceConfigurationMap.hxx
new file mode 100644 (file)
index 0000000..27a3a8a
--- /dev/null
@@ -0,0 +1,48 @@
+// FGDeviceConfigurationMap.hxx -- a map to access xml device configuration
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef _FGDEVICECONFIGURATIONMAP_HXX
+#define _FGDEVICECONFIGURATIONMAP_HXX
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif
+
+#include <simgear/props/props.hxx>
+#include <simgear/misc/sg_path.hxx>
+
+#include <map>
+using std::map;
+
+class FGDeviceConfigurationMap : public map<string,SGPropertyNode_ptr> {
+public:
+  FGDeviceConfigurationMap ( const char * relative_path, SGPropertyNode_ptr base, const char * childname );
+  virtual ~FGDeviceConfigurationMap();
+private:
+  void scan_dir( SGPath & path, int *index);
+  SGPropertyNode_ptr base;
+  const char * childname;
+};
+
+#endif
diff --git a/src/Input/FGEventInput.cxx b/src/Input/FGEventInput.cxx
new file mode 100644 (file)
index 0000000..bf04136
--- /dev/null
@@ -0,0 +1,203 @@
+// FGEventInput.cxx -- handle event driven input devices
+//
+// Written by Torsten Dreyer, started July 2009.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGEventInput.hxx"
+#include <Main/fg_props.hxx>
+#include <simgear/io/sg_file.hxx>
+#include <poll.h>
+#include <linux/input.h>
+
+static inline bool StartsWith( string & s, const char * cp )
+{
+  return s.compare( 0, strlen(cp), cp ) == 0;
+}
+
+FGInputEvent * FGInputEvent::NewObject( SGPropertyNode_ptr node )
+{
+  string name = node->getStringValue( "name" );
+  if( StartsWith( name, "button-" ) )
+    return new FGButtonEvent( node );
+
+  if( StartsWith( name, "rel-" ) )
+    return new FGAxisEvent( node );
+
+  if( StartsWith( name, "abs-" ) )
+    return new FGAxisEvent( node );
+
+  return NULL;
+}
+
+FGInputEvent::FGInputEvent( SGPropertyNode_ptr node ) :
+  lastDt(0.0)
+{
+  name = node->getStringValue( "name" );
+  desc = node->getStringValue( "desc" );
+  intervalSec = node->getDoubleValue("interval-sec",0.0);
+  string module = "event";
+  read_bindings( node, bindings, KEYMOD_NONE, module );
+}
+
+FGInputEvent::~FGInputEvent()
+{
+}
+
+void FGInputEvent::fire( FGEventData & eventData )
+{
+  lastDt += eventData.dt;
+  if( lastDt >= intervalSec ) {
+
+    for( binding_list_t::iterator it = bindings[KEYMOD_NONE].begin(); it != bindings[KEYMOD_NONE].end(); it++ )
+      (*it)->fire( eventData.value, 1.0 );
+
+    lastDt -= intervalSec;
+  }
+}
+
+FGAxisEvent::FGAxisEvent( SGPropertyNode_ptr node ) :
+  FGInputEvent( node )
+{
+  tolerance = node->getDoubleValue("tolerance", 0.002);
+  minRange = node->getDoubleValue("min-range", -1024.0);
+  maxRange = node->getDoubleValue("max-range", 1024.0);
+  center = node->getDoubleValue("center", 0.0);
+  deadband = node->getDoubleValue("dead-band", 0.0);
+  lowThreshold = node->getDoubleValue("low-threshold", -0.9);
+  highThreshold = node->getDoubleValue("high-threshold", 0.9);
+  lastValue = 9999999;
+}
+
+void FGAxisEvent::fire( FGEventData & eventData )
+{
+  if (fabs( eventData.value - lastValue) < tolerance)
+    return;
+  lastValue = eventData.value;
+  FGInputEvent::fire( eventData );
+}
+
+FGButtonEvent::FGButtonEvent( SGPropertyNode_ptr node ) :
+  FGInputEvent( node )
+{
+}
+
+void FGButtonEvent::fire( FGEventData & eventData )
+{
+  FGInputEvent::fire( eventData );
+}
+
+FGInputDevice::~FGInputDevice()
+{
+} 
+
+void FGInputDevice::HandleEvent( FGEventData & eventData )
+{
+  string eventName = TranslateEventName( eventData );  
+  cout << GetName() << " has event " << eventName << endl;
+  if( handledEvents.count( eventName ) > 0 ) {
+    handledEvents[ eventName ]->fire( eventData );
+  }
+}
+
+void FGInputDevice::SetName( string name )
+{
+  this->name = name; 
+}
+
+const char * FGEventInput::PROPERTY_ROOT = "/input/event";
+
+FGEventInput::FGEventInput() : 
+  configMap( "Input/Event", fgGetNode( PROPERTY_ROOT, true ), "device-named" )
+{
+}
+
+FGEventInput::~FGEventInput()
+{
+  for( map<int,FGInputDevice*>::iterator it = input_devices.begin(); it != input_devices.end(); it++ )
+    delete (*it).second;
+  input_devices.clear();
+}
+
+void FGEventInput::init( )
+{
+  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing event bindings");
+  SGPropertyNode * base = fgGetNode("/input/event", true);
+
+}
+
+void FGEventInput::postinit ()
+{
+}
+
+void FGEventInput::AddDevice( FGInputDevice * inputDevice )
+{
+  SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
+  SGPropertyNode_ptr deviceNode = NULL;
+
+  // look for configuration in the device map
+  if( configMap.count( inputDevice->GetName() ) > 0 ) {
+    // found - copy to /input/event/device[n]
+
+    // find a free index
+    unsigned index;
+    for( index = 0; index < 1000; index++ )
+      if( (deviceNode = baseNode->getNode( "device", index, false ) ) == NULL )
+        break;
+
+    if( index == 1000 ) {
+      SG_LOG(SG_INPUT, SG_WARN, "To many event devices - ignoring " << inputDevice->GetName() );
+      return;
+    }
+
+    // create this node 
+    deviceNode = baseNode->getNode( "device", index, true );
+
+    // and copy the properties from the configuration tree
+    copyProperties( configMap[ inputDevice->GetName() ], deviceNode );
+  }
+
+  if( deviceNode == NULL ) {
+    SG_LOG(SG_INPUT, SG_WARN, "No configuration found for device " << inputDevice->GetName() );
+    return;
+  }
+
+  vector<SGPropertyNode_ptr> eventNodes = deviceNode->getChildren( "event" );
+  for( vector<SGPropertyNode_ptr>::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ ) {
+    FGInputEvent * p = FGInputEvent::NewObject( *it );
+    if( p == NULL ) {
+      SG_LOG(SG_INPUT, SG_WARN, "Unhandled event/name in " << inputDevice->GetName()  );
+      continue;
+    }
+    inputDevice->AddHandledEvent( p );
+  }
+
+  // TODO:
+  // add nodes for the last event:
+  // last-event/name [string]
+  // last-event/value [double]
+  
+  try {        
+    inputDevice->Open();
+    input_devices[ deviceNode->getIndex() ] = inputDevice;
+  }
+  catch( ... ) {
+    SG_LOG(SG_INPUT, SG_WARN, "can't open InputDevice " << inputDevice->GetName()  );
+  }
+}
diff --git a/src/Input/FGEventInput.hxx b/src/Input/FGEventInput.hxx
new file mode 100644 (file)
index 0000000..8b4e0d9
--- /dev/null
@@ -0,0 +1,171 @@
+// FGEventInput.hxx -- handle event driven input devices
+//
+// Written by Torsten Dreyer, started July 2009
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef __FGEVENTINPUT_HXX
+#define __FGEVENTINPUT_HXX
+
+#include "FGCommonInput.hxx"
+#include "FGButton.hxx"
+#include "FGDeviceConfigurationMap.hxx"
+#include <simgear/structure/subsystem_mgr.hxx>
+
+/*
+ * A base class for event data. 
+ */
+struct FGEventData {
+  FGEventData( double aValue, double aDt ) : value(aValue), dt(aDt) {}
+  double value;
+  double dt;
+};
+
+/*
+ * A wrapper class for a configured event. 
+ * 
+ * <event>
+ *   <desc>Change the view pitch</desc>
+ *   <name>rel-x-rotate</name>
+ *   <binding>
+ *     <command>property-adjust</command>
+ *     <property>sim/current-view/pitch-offset-deg</property>
+ *     <factor type="double">0.01</factor>
+ *     <min type="double">-90.0</min>
+ *     <max type="double">90.0</max>
+ *     <wrap type="bool">false</wrap>
+ *   </binding>
+ * </event>
+ */
+class FGInputEvent : public SGReferenced,FGCommonInput {
+public:
+  /*
+   * Constructor for the class. The arg node shall point
+   * to the property corresponding to the <event>  node
+   */
+  FGInputEvent( SGPropertyNode_ptr node );
+  virtual ~FGInputEvent();
+
+  /*
+   * dispatch the event value through all bindings 
+   */
+  virtual void fire( FGEventData & eventData );
+
+  /*
+   * access for the name property
+   */
+  string GetName() const { return name; }
+
+  /*
+   * access for the description property
+   */
+  string GetDescription() const { return desc; }
+
+  static FGInputEvent * NewObject( SGPropertyNode_ptr node );
+
+protected:
+  /* A more or less meaningfull description of the event */
+  string desc;
+
+  /* One of the predefined names of the event */
+  string name;
+
+  /* A list of SGBinding objects */
+  binding_list_t bindings[KEYMOD_MAX];
+
+  double lastDt;
+  double intervalSec;
+};
+
+class FGButtonEvent : public FGInputEvent {
+public:
+  FGButtonEvent( SGPropertyNode_ptr node );
+  virtual void fire( FGEventData & eventData );
+};
+
+class FGAxisEvent : public FGInputEvent {
+public:
+  FGAxisEvent( SGPropertyNode_ptr node );
+protected:
+  virtual void fire( FGEventData & eventData );
+  double tolerance;
+  double minRange;
+  double maxRange;
+  double center;
+  double deadband;
+  double lowThreshold;
+  double highThreshold;
+  double lastValue;
+};
+
+typedef class SGSharedPtr<FGInputEvent> FGInputEvent_ptr;
+
+/*
+ * A abstract class implementing basic functionality of input devices for
+ * all operating systems. This is the base class for the O/S-specific
+ * implementation of input device handlers
+ */
+class FGInputDevice : public SGReferenced {
+public:
+  FGInputDevice() {}
+  FGInputDevice( string aName ) : name(aName) {}
+    
+  virtual ~FGInputDevice();
+
+  virtual void Open() = 0;
+  virtual void Close() = 0;
+  virtual const char * TranslateEventName( FGEventData & eventData ) = 0;
+
+
+  void SetName( string name );
+  string & GetName() { return name; }
+
+  void HandleEvent( FGEventData & eventData );
+  void AddHandledEvent( FGInputEvent_ptr handledEvent ) {
+    if( handledEvents.count( handledEvent->GetName() ) == 0 )
+      handledEvents[handledEvent->GetName()] = handledEvent;
+  }
+
+private:
+  map<string,FGInputEvent_ptr> handledEvents;
+  string name;
+};
+
+typedef SGSharedPtr<FGInputDevice> FGInputDevice_ptr;
+
+
+/*
+ * The Subsystem for the event input device 
+ */
+class FGEventInput : public SGSubsystem,FGCommonInput {
+public:
+  FGEventInput();
+  virtual ~FGEventInput();
+  virtual void init();
+  virtual void postinit();
+
+protected:
+  static const char * PROPERTY_ROOT;
+
+  void AddDevice( FGInputDevice * inputDevice );
+  map<int,FGInputDevice*> input_devices;
+  FGDeviceConfigurationMap configMap;
+};
+
+#endif
diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx
new file mode 100644 (file)
index 0000000..75dc887
--- /dev/null
@@ -0,0 +1,306 @@
+// FGJoystickInput.cxx -- handle user input from joystick devices
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGJoystickInput.hxx"
+#include "FGDeviceConfigurationMap.hxx"
+#include <Main/fg_props.hxx>
+#include <Scripting/NasalSys.hxx>
+
+FGJoystickInput::axis::axis ()
+  : last_value(9999999),
+    tolerance(0.002),
+    low_threshold(-0.9),
+    high_threshold(0.9),
+    interval_sec(0),
+    last_dt(0)
+{
+}
+
+FGJoystickInput::axis::~axis ()
+{
+}
+
+FGJoystickInput::joystick::joystick ()
+  : jsnum(0),
+    js(0),
+    naxes(0),
+    nbuttons(0),
+    axes(0),
+    buttons(0)
+{
+}
+
+FGJoystickInput::joystick::~joystick ()
+{
+//  delete js? why not?
+//   delete js;
+  delete[] axes;
+  delete[] buttons;
+}
+
+
+FGJoystickInput::FGJoystickInput()
+{
+}
+
+FGJoystickInput::~FGJoystickInput()
+{
+}
+
+
+void FGJoystickInput::init()
+{
+  jsInit();
+                                // TODO: zero the old bindings first.
+  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
+  SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
+
+  FGDeviceConfigurationMap configMap("Input/Joysticks", js_nodes, "js-named");
+
+  for (int i = 0; i < MAX_JOYSTICKS; i++) {
+    jsJoystick * js = new jsJoystick(i);
+    bindings[i].js = js;
+
+    if (js->notWorking()) {
+      SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
+      continue;
+    }
+
+    const char * name = js->getName();
+    SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
+
+    if (js_node) {
+      SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
+
+    } else {
+      SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
+      SGPropertyNode_ptr named;
+
+      if ((named = configMap[name])) {
+        string source = named->getStringValue("source", "user defined");
+        SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
+
+      } else if ((named = configMap["default"])) {
+        string source = named->getStringValue("source", "user defined");
+        SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
+            << "\"\nUsing default: \"" << source << '"');
+
+      } else {
+        SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
+      }
+
+      js_node = js_nodes->getChild("js", i, true);
+      copyProperties(named, js_node);
+      js_node->setStringValue("id", name);
+    }
+  }
+}
+
+void FGJoystickInput::postinit()
+{
+  FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
+  SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
+
+  for (int i = 0; i < MAX_JOYSTICKS; i++) {
+    SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
+    jsJoystick *js = bindings[i].js;
+    if (!js_node || js->notWorking())
+      continue;
+
+#ifdef WIN32
+    JOYCAPS jsCaps ;
+    joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
+    unsigned int nbuttons = jsCaps.wNumButtons;
+    if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
+#else
+    unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
+#endif
+
+    int naxes = js->getNumAxes();
+    if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
+    bindings[i].naxes = naxes;
+    bindings[i].nbuttons = nbuttons;
+
+    SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
+
+                                // Set up range arrays
+    float minRange[MAX_JOYSTICK_AXES];
+    float maxRange[MAX_JOYSTICK_AXES];
+    float center[MAX_JOYSTICK_AXES];
+
+                                // Initialize with default values
+    js->getMinRange(minRange);
+    js->getMaxRange(maxRange);
+    js->getCenter(center);
+
+                                // Allocate axes and buttons
+    bindings[i].axes = new axis[naxes];
+    bindings[i].buttons = new FGButton[nbuttons];
+
+    //
+    // Initialize nasal groups.
+    //
+    ostringstream str;
+    str << "__js" << i;
+    string module = str.str();
+    nasalsys->createModule(module.c_str(), module.c_str(), "", 0);
+
+    vector<SGPropertyNode_ptr> nasal = js_node->getChildren("nasal");
+    unsigned int j;
+    for (j = 0; j < nasal.size(); j++) {
+      nasal[j]->setStringValue("module", module.c_str());
+      nasalsys->handleCommand(nasal[j]);
+    }
+
+    //
+    // Initialize the axes.
+    //
+    vector<SGPropertyNode_ptr> axes = js_node->getChildren("axis");
+    size_t nb_axes = axes.size();
+    for (j = 0; j < nb_axes; j++ ) {
+      const SGPropertyNode * axis_node = axes[j];
+      const SGPropertyNode * num_node = axis_node->getChild("number");
+      int n_axis = axis_node->getIndex();
+      if (num_node != 0) {
+          n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
+
+          // Silently ignore platforms that are not specified within the
+          // <number></number> section
+          if (n_axis < 0)
+             continue;
+      }
+
+      if (n_axis >= naxes) {
+          SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
+          continue;
+      }
+      axis &a = bindings[i].axes[n_axis];
+
+      js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
+
+      a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
+      minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
+      maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
+      center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
+
+      read_bindings(axis_node, a.bindings, KEYMOD_NONE, module );
+
+      // Initialize the virtual axis buttons.
+      a.low.init(axis_node->getChild("low"), "low", module );
+      a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
+
+      a.high.init(axis_node->getChild("high"), "high", module );
+      a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
+      a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
+      a.last_dt = 0.0;
+    }
+
+    //
+    // Initialize the buttons.
+    //
+    vector<SGPropertyNode_ptr> buttons = js_node->getChildren("button");
+    char buf[32];
+    for (j = 0; j < buttons.size() && j < nbuttons; j++) {
+      const SGPropertyNode * button_node = buttons[j];
+      const SGPropertyNode * num_node = button_node->getChild("number");
+      size_t n_but = button_node->getIndex();
+      if (num_node != 0) {
+          n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
+      }
+
+      if (n_but >= nbuttons) {
+          SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
+          continue;
+      }
+
+      sprintf(buf, "%d", n_but);
+      SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << n_but);
+      bindings[i].buttons[n_but].init(button_node, buf, module );
+
+      // get interval-sec property
+      FGButton &b = bindings[i].buttons[n_but];
+      if (button_node != 0) {
+        b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
+        b.last_dt = 0.0;
+      }
+    }
+
+    js->setMinRange(minRange);
+    js->setMaxRange(maxRange);
+    js->setCenter(center);
+  }
+}
+
+void FGJoystickInput::update( double dt )
+{
+  float axis_values[MAX_JOYSTICK_AXES];
+  int modifiers = fgGetKeyModifiers();
+  int buttons;
+
+  for (int i = 0; i < MAX_JOYSTICKS; i++) {
+
+    jsJoystick * js = bindings[i].js;
+    if (js == 0 || js->notWorking())
+      continue;
+
+    js->read(&buttons, axis_values);
+
+                                // Fire bindings for the axes.
+    for (int j = 0; j < bindings[i].naxes; j++) {
+      axis &a = bindings[i].axes[j];
+
+                                // Do nothing if the axis position
+                                // is unchanged; only a change in
+                                // position fires the bindings.
+      if (fabs(axis_values[j] - a.last_value) > a.tolerance) {
+        a.last_value = axis_values[j];
+        for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
+          a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
+      }
+
+                                // do we have to emulate axis buttons?
+      a.last_dt += dt;
+      if(a.last_dt >= a.interval_sec) {
+        if (a.low.bindings[modifiers].size())
+          bindings[i].axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
+
+        if (a.high.bindings[modifiers].size())
+          bindings[i].axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
+
+        a.last_dt -= a.interval_sec;
+      }
+    }
+
+                                // Fire bindings for the buttons.
+    for (int j = 0; j < bindings[i].nbuttons; j++) {
+      FGButton &b = bindings[i].buttons[j];
+      b.last_dt += dt;
+      if(b.last_dt >= b.interval_sec) {
+        bindings[i].buttons[j].update( modifiers, (buttons & (1 << j)) > 0 );
+        b.last_dt -= b.interval_sec;
+      }
+    }
+  }
+}
+
diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx
new file mode 100644 (file)
index 0000000..ef1530e
--- /dev/null
@@ -0,0 +1,88 @@
+// FGJoystickInput.hxx -- handle user input from joystick devices
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef _FGJOYSTICKINPUT_HXX
+#define _FGJOYSTICKINPUT_HXX
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif
+
+#include "FGCommonInput.hxx"
+#include "FGButton.hxx"
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <plib/js.h>
+
+////////////////////////////////////////////////////////////////////////
+// The Joystick Input Class
+////////////////////////////////////////////////////////////////////////
+class FGJoystickInput : public SGSubsystem,FGCommonInput {
+public:
+  FGJoystickInput();
+  virtual ~FGJoystickInput();
+
+  virtual void init();
+  virtual void postinit();
+  virtual void update( double dt );
+
+  static const int MAX_JOYSTICKS        = 10;
+  static const int MAX_JOYSTICK_AXES    = _JS_MAX_AXES;
+  static const int MAX_JOYSTICK_BUTTONS = 32;
+
+private:
+  /**
+   * Settings for a single joystick axis.
+   */
+  struct axis {
+    axis ();
+    virtual ~axis ();
+    float last_value;
+    float tolerance;
+    binding_list_t bindings[KEYMOD_MAX];
+    float low_threshold;
+    float high_threshold;
+    FGButton low;
+    FGButton high;
+    float interval_sec;
+    double last_dt;
+  };
+
+  /**
+   * Settings for a joystick.
+   */
+  struct joystick {
+    joystick ();
+    virtual ~joystick ();
+    int jsnum;
+    jsJoystick * js;
+    int naxes;
+    int nbuttons;
+    axis * axes;
+    FGButton * buttons;
+  };
+  joystick bindings[MAX_JOYSTICKS];
+
+};
+
+#endif
diff --git a/src/Input/FGKeyboardInput.cxx b/src/Input/FGKeyboardInput.cxx
new file mode 100644 (file)
index 0000000..f4d20d6
--- /dev/null
@@ -0,0 +1,259 @@
+// FGKeyboardInput.cxx -- handle user input from keyboard devices
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGKeyboardInput.hxx"
+#include <Main/fg_props.hxx>
+#include <Scripting/NasalSys.hxx>
+#include <plib/pu.h>
+
+static int getModifiers ()
+{
+  return fgGetKeyModifiers() >> 1;
+}
+
+static bool getModShift ()
+{
+  return (fgGetKeyModifiers() & KEYMOD_SHIFT) != 0;
+}
+
+static bool getModCtrl ()
+{
+  return (fgGetKeyModifiers() & KEYMOD_CTRL) != 0;
+}
+
+static bool getModAlt ()
+{
+  return (fgGetKeyModifiers() & KEYMOD_ALT) != 0;
+}
+
+static bool getModMeta ()
+{
+  return (fgGetKeyModifiers() & KEYMOD_META) != 0;
+}
+
+static bool getModSuper ()
+{
+  return (fgGetKeyModifiers() & KEYMOD_SUPER) != 0;
+}
+
+static bool getModHyper ()
+{
+  return (fgGetKeyModifiers() & KEYMOD_HYPER) != 0;
+}
+
+FGKeyboardInput * FGKeyboardInput::keyboardInput = NULL;
+
+FGKeyboardInput::FGKeyboardInput() :
+    _key_event(fgGetNode("/devices/status/keyboard/event", true))
+{
+  if( keyboardInput == NULL )
+    keyboardInput = this;
+}
+
+FGKeyboardInput::~FGKeyboardInput()
+{
+  if( keyboardInput == this )
+    keyboardInput = NULL;
+}
+
+
+void FGKeyboardInput::init()
+{
+  fgRegisterKeyHandler(keyHandler);
+}
+
+void FGKeyboardInput::postinit()
+{
+  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
+  string module = "__kbd";
+  SGPropertyNode * key_nodes = fgGetNode("/input/keyboard");
+  if (key_nodes == NULL) {
+    SG_LOG(SG_INPUT, SG_WARN, "No key bindings (/input/keyboard)!!");
+    key_nodes = fgGetNode("/input/keyboard", true);
+  }
+
+  FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
+  vector<SGPropertyNode_ptr> nasal = key_nodes->getChildren("nasal");
+  for (unsigned int j = 0; j < nasal.size(); j++) {
+    nasal[j]->setStringValue("module", module.c_str());
+    nasalsys->handleCommand(nasal[j]);
+  }
+
+  vector<SGPropertyNode_ptr> keys = key_nodes->getChildren("key");
+  for (unsigned int i = 0; i < keys.size(); i++) {
+    int index = keys[i]->getIndex();
+    SG_LOG(SG_INPUT, SG_DEBUG, "Binding key " << index);
+    if( index >= MAX_KEYS ) {
+      SG_LOG(SG_INPUT, SG_WARN, "Key binding " << index << " out of range");
+      continue;
+    }
+
+    bindings[index].bindings->clear();
+    bindings[index].is_repeatable = keys[i]->getBoolValue("repeatable");
+    bindings[index].last_state = 0;
+    read_bindings(keys[i], bindings[index].bindings, KEYMOD_NONE, module );
+  }
+}
+
+void FGKeyboardInput::bind()
+{
+  fgTie("/devices/status/keyboard", getModifiers);
+  fgTie("/devices/status/keyboard/shift", getModShift);
+  fgTie("/devices/status/keyboard/ctrl", getModCtrl);
+  fgTie("/devices/status/keyboard/alt", getModAlt);
+  fgTie("/devices/status/keyboard/meta", getModMeta);
+  fgTie("/devices/status/keyboard/super", getModSuper);
+  fgTie("/devices/status/keyboard/hyper", getModHyper);
+
+  _key_event->tie("key", SGRawValuePointer<int>(&_key_code));
+  _key_event->tie("pressed", SGRawValuePointer<bool>(&_key_pressed));
+  _key_event->tie("modifier", SGRawValuePointer<int>(&_key_modifiers));
+  _key_event->tie("modifier/shift", SGRawValuePointer<bool>(&_key_shift));
+  _key_event->tie("modifier/ctrl", SGRawValuePointer<bool>(&_key_ctrl));
+  _key_event->tie("modifier/alt", SGRawValuePointer<bool>(&_key_alt));
+  _key_event->tie("modifier/meta", SGRawValuePointer<bool>(&_key_meta));
+  _key_event->tie("modifier/super", SGRawValuePointer<bool>(&_key_super));
+  _key_event->tie("modifier/hyper", SGRawValuePointer<bool>(&_key_hyper));
+}
+
+void FGKeyboardInput::unbind()
+{
+  fgUntie("/devices/status/keyboard");
+  fgUntie("/devices/status/keyboard/shift");
+  fgUntie("/devices/status/keyboard/ctrl");
+  fgUntie("/devices/status/keyboard/alt");
+  fgUntie("/devices/status/keyboard/meta");
+  fgUntie("/devices/status/keyboard/super");
+  fgUntie("/devices/status/keyboard/hyper");
+
+  _key_event->untie("key");
+  _key_event->untie("pressed");
+  _key_event->untie("modifier");
+  _key_event->untie("modifier/shift");
+  _key_event->untie("modifier/ctrl");
+  _key_event->untie("modifier/alt");
+  _key_event->untie("modifier/meta");
+  _key_event->untie("modifier/super");
+  _key_event->untie("modifier/hyper");
+}
+
+void FGKeyboardInput::update( double dt )
+{
+  // nothing to do
+}
+
+const FGCommonInput::binding_list_t & FGKeyboardInput::_find_key_bindings (unsigned int k, int modifiers)
+{
+  unsigned char kc = (unsigned char)k;
+  FGButton &b = bindings[k];
+
+                                // Try it straight, first.
+  if (b.bindings[modifiers].size() > 0)
+    return b.bindings[modifiers];
+
+                                // Alt-Gr is CTRL+ALT
+  else if (modifiers&(KEYMOD_CTRL|KEYMOD_ALT))
+    return _find_key_bindings(k, modifiers&~(KEYMOD_CTRL|KEYMOD_ALT));
+
+                                // Try removing the control modifier
+                                // for control keys.
+  else if ((modifiers&KEYMOD_CTRL) && iscntrl(kc))
+    return _find_key_bindings(k, modifiers&~KEYMOD_CTRL);
+
+                                // Try removing shift modifier 
+                                // for upper case or any punctuation
+                                // (since different keyboards will
+                                // shift different punctuation types)
+  else if ((modifiers&KEYMOD_SHIFT) && (isupper(kc) || ispunct(kc)))
+    return _find_key_bindings(k, modifiers&~KEYMOD_SHIFT);
+
+                                // Try removing alt modifier for
+                                // high-bit characters.
+  else if ((modifiers&KEYMOD_ALT) && k >= 128 && k < 256)
+    return _find_key_bindings(k, modifiers&~KEYMOD_ALT);
+
+                                // Give up and return the empty vector.
+  else
+    return b.bindings[modifiers];
+}
+
+void FGKeyboardInput::doKey (int k, int modifiers, int x, int y)
+{
+  // Sanity check.
+  if (k < 0 || k >= MAX_KEYS) {
+    SG_LOG(SG_INPUT, SG_WARN, "Key value " << k << " out of range");
+    return;
+  }
+
+  _key_code = k;
+  _key_modifiers = modifiers >> 1;
+  _key_pressed = (modifiers & KEYMOD_RELEASED) == 0;
+  _key_shift = getModShift();
+  _key_ctrl = getModCtrl();
+  _key_alt = getModAlt();
+  _key_meta = getModMeta();
+  _key_super = getModSuper();
+  _key_hyper = getModHyper();
+  _key_event->fireValueChanged();
+  if (_key_code < 0)
+    return;
+
+  k = _key_code;
+  modifiers = _key_modifiers << 1;
+  if (!_key_pressed)
+      modifiers |= KEYMOD_RELEASED;
+  FGButton &b = bindings[k];
+
+                                // Key pressed.
+  if (!(modifiers & KEYMOD_RELEASED)) {
+    SG_LOG( SG_INPUT, SG_DEBUG, "User pressed key " << k << " with modifiers " << modifiers );
+    if (!b.last_state || b.is_repeatable) {
+      const binding_list_t &bindings = _find_key_bindings(k, modifiers);
+
+      for (unsigned int i = 0; i < bindings.size(); i++)
+        bindings[i]->fire();
+      b.last_state = 1;
+    }
+  }
+                                // Key released.
+  else {
+    SG_LOG(SG_INPUT, SG_DEBUG, "User released key " << k << " with modifiers " << modifiers);
+    if (b.last_state) {
+      const binding_list_t &bindings = _find_key_bindings(k, modifiers);
+      for (unsigned int i = 0; i < bindings.size(); i++)
+        bindings[i]->fire();
+      b.last_state = 0;
+    }
+  }
+}
+
+void FGKeyboardInput::keyHandler(int key, int keymod, int mousex, int mousey)
+{
+  if((keymod & KEYMOD_RELEASED) == 0)
+      if(puKeyboard(key, PU_DOWN))
+          return;
+
+  if(keyboardInput)
+      keyboardInput->doKey(key, keymod, mousex, mousey);
+}
diff --git a/src/Input/FGKeyboardInput.hxx b/src/Input/FGKeyboardInput.hxx
new file mode 100644 (file)
index 0000000..225d0f6
--- /dev/null
@@ -0,0 +1,71 @@
+// FGKeyboardInput.hxx -- handle user input from keyboard devices
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#ifndef _FGKEYBOARDINPUT_HXX
+#define _FGKEYBOARDINPUT_HXX
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif
+
+#include "FGCommonInput.hxx"
+#include "FGButton.hxx"
+#include <simgear/structure/subsystem_mgr.hxx>
+
+////////////////////////////////////////////////////////////////////////
+// The Keyboard Input Class
+////////////////////////////////////////////////////////////////////////
+class FGKeyboardInput : public SGSubsystem,FGCommonInput {
+public:
+  FGKeyboardInput();
+  virtual ~FGKeyboardInput();
+
+  virtual void init();
+  virtual void postinit();
+  virtual void bind();
+  virtual void unbind();
+  virtual void update( double dt );
+
+  static const int MAX_KEYS = 1024;
+
+private:
+  const binding_list_t& _find_key_bindings (unsigned int k, int modifiers);
+  void doKey (int k, int modifiers, int x, int y);
+
+  static void keyHandler(int key, int keymod, int mousex, int mousey);
+  static FGKeyboardInput * keyboardInput;
+  FGButton bindings[MAX_KEYS];
+  SGPropertyNode_ptr _key_event;
+  int  _key_code;
+  int  _key_modifiers;
+  bool _key_pressed;
+  bool _key_shift;
+  bool _key_ctrl;
+  bool _key_alt;
+  bool _key_meta;
+  bool _key_super;
+  bool _key_hyper;
+};
+
+#endif
diff --git a/src/Input/FGMouseInput.cxx b/src/Input/FGMouseInput.cxx
new file mode 100644 (file)
index 0000000..c0b7e3f
--- /dev/null
@@ -0,0 +1,363 @@
+// FGMouseInput.cxx -- handle user input from mouse devices
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+#include "FGMouseInput.hxx"
+
+\f
+void ActivePickCallbacks::init( int b, const osgGA::GUIEventAdapter* ea ) 
+{
+  // Get the list of hit callbacks. Take the first callback that
+  // accepts the mouse button press and ignore the rest of them
+  // That is they get sorted by distance and by scenegraph depth.
+  // The nearest one is the first one and the deepest
+  // (the most specialized one in the scenegraph) is the first.
+  std::vector<SGSceneryPick> pickList;
+  if (FGRenderer::pick(pickList, ea)) {
+    std::vector<SGSceneryPick>::const_iterator i;
+    for (i = pickList.begin(); i != pickList.end(); ++i) {
+      if (i->callback->buttonPressed(b, i->info)) {
+          (*this)[b].push_back(i->callback);
+          return;
+      }
+    }
+  }
+}
+
+void ActivePickCallbacks::update( double dt ) 
+{
+  // handle repeatable mouse press events
+  for( iterator mi = begin(); mi != end(); ++mi ) {
+    std::list<SGSharedPtr<SGPickCallback> >::iterator li;
+    for (li = mi->second.begin(); li != mi->second.end(); ++li) {
+      (*li)->update(dt);
+    }
+  }
+}
+
+
+#include <plib/pu.h>
+#include <Model/panelnode.hxx>
+#include <Cockpit/panel.hxx>
+////////////////////////////////////////////////////////////////////////
+// The Mouse Input Implementation
+////////////////////////////////////////////////////////////////////////
+
+const FGMouseInput::MouseCursorMap FGMouseInput::mouse_cursor_map[] = {
+    { "none", MOUSE_CURSOR_NONE },
+    { "inherit", MOUSE_CURSOR_POINTER },
+    { "wait", MOUSE_CURSOR_WAIT },
+    { "crosshair", MOUSE_CURSOR_CROSSHAIR },
+    { "left-right", MOUSE_CURSOR_LEFTRIGHT },
+    { 0, 0 }
+};
+
+FGMouseInput * FGMouseInput::mouseInput = NULL;
+
+FGMouseInput::FGMouseInput()
+{
+  if( mouseInput == NULL )
+    mouseInput = this;
+}
+
+FGMouseInput::~FGMouseInput()
+{
+  if( mouseInput == this )
+    mouseInput = NULL;
+}
+
+void FGMouseInput::init()
+{
+  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
+  string module = "";
+
+  SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
+  if (mouse_nodes == 0) {
+    SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
+    mouse_nodes = fgGetNode("/input/mice", true);
+  }
+
+  int j;
+  for (int i = 0; i < MAX_MICE; i++) {
+    SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
+    mouse &m = bindings[i];
+
+                                // Grab node pointers
+    char buf[64];
+    sprintf(buf, "/devices/status/mice/mouse[%d]/mode", i);
+    m.mode_node = fgGetNode(buf);
+    if (m.mode_node == NULL) {
+      m.mode_node = fgGetNode(buf, true);
+      m.mode_node->setIntValue(0);
+    }
+    for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
+      sprintf(buf, "/devices/status/mice/mouse[%d]/button[%d]", i, j);
+      m.mouse_button_nodes[j] = fgGetNode(buf, true);
+      m.mouse_button_nodes[j]->setBoolValue(false);
+    }
+
+                                // Read all the modes
+    m.nModes = mouse_node->getIntValue("mode-count", 1);
+    m.modes = new mouse_mode[m.nModes];
+
+    for (int j = 0; j < m.nModes; j++) {
+      int k;
+
+                                // Read the mouse cursor for this mode
+      SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
+      const char * cursor_name =
+        mode_node->getStringValue("cursor", "inherit");
+      m.modes[j].cursor = MOUSE_CURSOR_POINTER;
+      for (k = 0; mouse_cursor_map[k].name != 0; k++) {
+        if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
+          m.modes[j].cursor = mouse_cursor_map[k].cursor;
+          break;
+        }
+      }
+
+                                // Read other properties for this mode
+      m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
+      m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
+
+                                // Read the button bindings for this mode
+      m.modes[j].buttons = new FGButton[MAX_MOUSE_BUTTONS];
+      char buf[32];
+      for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
+        sprintf(buf, "mouse button %d", k);
+        SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
+        m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf, module );
+      }
+
+                                // Read the axis bindings for this mode
+      read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
+      read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
+    }
+  }
+
+  fgRegisterMouseClickHandler(mouseClickHandler);
+  fgRegisterMouseMotionHandler(mouseMotionHandler);
+}
+
+void FGMouseInput::update ( double dt )
+{
+  mouse &m = bindings[0];
+  int mode =  m.mode_node->getIntValue();
+  if (mode != m.current_mode) {
+    m.current_mode = mode;
+    m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
+    if (mode >= 0 && mode < m.nModes) {
+      fgSetMouseCursor(m.modes[mode].cursor);
+      m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
+      m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
+      fgWarpMouse(m.x, m.y);
+    } else {
+      SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
+      fgSetMouseCursor(MOUSE_CURSOR_POINTER);
+    }
+  }
+
+  if ( fgGetBool( "/sim/mouse/hide-cursor", true ) ) {
+      if ( m.x != m.save_x || m.y != m.save_y ) {
+          m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
+          if (fgGetMouseCursor() == MOUSE_CURSOR_NONE)
+              fgSetMouseCursor(m.modes[mode].cursor);
+      } else {
+          m.timeout -= dt;
+          if ( m.timeout <= 0.0 ) {
+              fgSetMouseCursor(MOUSE_CURSOR_NONE);
+              m.timeout = 0.0;
+          }
+      }
+      m.save_x = m.x;
+      m.save_y = m.y;
+  }
+
+  activePickCallbacks.update( dt );
+}
+
+FGMouseInput::mouse::mouse ()
+  : x(-1),
+    y(-1),
+    save_x(-1),
+    save_y(-1),
+    nModes(1),
+    current_mode(0),
+    modes(NULL)
+{
+}
+
+FGMouseInput::mouse::~mouse ()
+{
+  delete [] modes;
+}
+
+FGMouseInput::mouse_mode::mouse_mode ()
+  : cursor(MOUSE_CURSOR_POINTER),
+    constrained(false),
+    pass_through(false),
+    buttons(NULL)
+{
+}
+
+FGMouseInput::mouse_mode::~mouse_mode ()
+{
+                                // FIXME: memory leak
+//   for (int i = 0; i < KEYMOD_MAX; i++) {
+//     int j;
+//     for (j = 0; i < x_bindings[i].size(); j++)
+//       delete bindings[i][j];
+//     for (j = 0; j < y_bindings[i].size(); j++)
+//       delete bindings[i][j];
+//   }
+  delete [] buttons;
+}
+
+void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
+{
+  int modifiers = fgGetKeyModifiers();
+
+  mouse &m = bindings[0];
+  mouse_mode &mode = m.modes[m.current_mode];
+
+                                // Let the property manager know.
+  if (b >= 0 && b < MAX_MOUSE_BUTTONS)
+    m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
+
+                                // Pass on to PUI and the panel if
+                                // requested, and return if one of
+                                // them consumes the event.
+
+  if (updown != MOUSE_BUTTON_DOWN) {
+    // Execute the mouse up event in any case, may be we should
+    // stop processing here?
+    while (!activePickCallbacks[b].empty()) {
+      activePickCallbacks[b].front()->buttonReleased();
+      activePickCallbacks[b].pop_front();
+    }
+  }
+
+  if (mode.pass_through) {
+    if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
+      return;
+    else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
+             globals->get_current_panel()->getVisibility() &&
+             globals->get_current_panel()->doMouseAction(b, updown, x, y))
+      return;
+    else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
+      return;
+    else {
+      // pui didn't want the click event so compute a
+      // scenegraph intersection point corresponding to the mouse click
+      if (updown == MOUSE_BUTTON_DOWN) {
+        activePickCallbacks.init( b, ea );
+      }
+    }
+  }
+
+  // OK, PUI and the panel didn't want the click
+  if (b >= MAX_MOUSE_BUTTONS) {
+    SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
+           << " where only " << MAX_MOUSE_BUTTONS << " expected");
+    return;
+  }
+
+  m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
+}
+
+void FGMouseInput::doMouseMotion (int x, int y)
+{
+  // Don't call fgGetKeyModifiers() here, until we are using a
+  // toolkit that supports getting the mods from outside a key
+  // callback.  Glut doesn't.
+  int modifiers = KEYMOD_NONE;
+
+  int xsize = fgGetInt("/sim/startup/xsize", 800);
+  int ysize = fgGetInt("/sim/startup/ysize", 600);
+
+  mouse &m = bindings[0];
+
+  if (m.current_mode < 0 || m.current_mode >= m.nModes) {
+      m.x = x;
+      m.y = y;
+      return;
+  }
+  mouse_mode &mode = m.modes[m.current_mode];
+
+                                // Pass on to PUI if requested, and return
+                                // if PUI consumed the event.
+  if (mode.pass_through && puMouse(x, y)) {
+      m.x = x;
+      m.y = y;
+      return;
+  }
+
+                                // OK, PUI didn't want the event,
+                                // so we can play with it.
+  if (x != m.x) {
+    int delta = x - m.x;
+    for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
+      mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
+  }
+  if (y != m.y) {
+    int delta = y - m.y;
+    for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
+      mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
+  }
+
+                                // Constrain the mouse if requested
+  if (mode.constrained) {
+    bool need_warp = false;
+    if (x <= (xsize * .25) || x >= (xsize * .75)) {
+      x = int(xsize * .5);
+      need_warp = true;
+    }
+
+    if (y <= (ysize * .25) || y >= (ysize * .75)) {
+      y = int(ysize * .5);
+      need_warp = true;
+    }
+
+    if (need_warp)
+      fgWarpMouse(x, y);
+  }
+
+  if (m.x != x)
+      fgSetInt("/devices/status/mice/mouse/x", m.x = x);
+
+  if (m.y != y)
+      fgSetInt("/devices/status/mice/mouse/y", m.y = y);
+}
+
+void FGMouseInput::mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
+{
+    if(mouseInput)
+      mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
+}
+
+void FGMouseInput::mouseMotionHandler(int x, int y)
+{
+    if (mouseInput != 0)
+        mouseInput->doMouseMotion(x, y);
+}
+
+
diff --git a/src/Input/FGMouseInput.hxx b/src/Input/FGMouseInput.hxx
new file mode 100644 (file)
index 0000000..7e1d5ab
--- /dev/null
@@ -0,0 +1,123 @@
+// FGMouseInput.hxx -- handle user input from mouse devices
+//
+// Written by Torsten Dreyer, started August 2009
+// Based on work from David Megginson, started May 2001.
+//
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
+// Copyright (C) 2001 David Megginson, david@megginson.com
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+// $Id$
+
+
+#ifndef _FGMOUSEINPUT_HXX
+#define _FGMOUSEINPUT_HXX
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif
+
+#include "FGCommonInput.hxx"
+#include "FGButton.hxx"
+
+#include <map>
+#include <list>
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/scene/util/SGPickCallback.hxx>
+#include <Main/renderer.hxx>
+/**
+  * List of currently pressed mouse button events
+  */
+class ActivePickCallbacks : public std::map<int, std::list<SGSharedPtr<SGPickCallback> > > {
+public:
+  void update( double dt );
+  void init( int b, const osgGA::GUIEventAdapter* ea );
+};
+
+
+////////////////////////////////////////////////////////////////////////
+// The Mouse Input Class
+////////////////////////////////////////////////////////////////////////
+class FGMouseInput : public SGSubsystem,FGCommonInput {
+public:
+  FGMouseInput();
+  virtual ~FGMouseInput();
+
+  virtual void init();
+  virtual void update( double dt );
+
+  static const int MAX_MICE = 1;
+  static const int MAX_MOUSE_BUTTONS = 8;
+
+private:
+  void doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea);
+  void doMouseMotion (int x, int y);
+  static FGMouseInput * mouseInput;
+  static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
+  static void mouseMotionHandler(int x, int y);
+
+  ActivePickCallbacks activePickCallbacks;
+  /**
+   * Settings for a mouse mode.
+   */
+  struct mouse_mode {
+    mouse_mode ();
+    virtual ~mouse_mode ();
+    int cursor;
+    bool constrained;
+    bool pass_through;
+    FGButton * buttons;
+    binding_list_t x_bindings[KEYMOD_MAX];
+    binding_list_t y_bindings[KEYMOD_MAX];
+  };
+
+
+  /**
+   * Settings for a mouse.
+   */
+  struct mouse {
+    mouse ();
+    virtual ~mouse ();
+    int x;
+    int y;
+    int save_x;
+    int save_y;
+    SGPropertyNode_ptr mode_node;
+    SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
+    int nModes;
+    int current_mode;
+    double timeout;
+    mouse_mode * modes;
+  };
+
+  // 
+  // Map of all known cursor names
+  // This used to contain all the Glut cursors, but those are
+  // not defined by other toolkits.  It now supports only the cursor
+  // images we actually use, in the interest of portability.  Someday,
+  // it would be cool to write an OpenGL cursor renderer, with the
+  // cursors defined as textures referenced in the property tree.  This
+  // list could then be eliminated. -Andy
+  //
+  const static struct MouseCursorMap {
+    const char * name;
+    int cursor;
+  } mouse_cursor_map[];
+
+  mouse bindings[MAX_MICE];
+};
+
+#endif
index df99e01f1de85f1b0ae52b22b5562235a33b2fc9..d51d2887c03d0c7a53e43d5998412454ab605b60 100644 (file)
@@ -2,7 +2,12 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkgdatadir)\"
 
 noinst_LIBRARIES = libInput.a
 
-libInput_a_SOURCES = input.cxx input.hxx
+libInput_a_SOURCES = input.cxx input.hxx FGCommonInput.cxx FGCommonInput.hxx \
+       FGDeviceConfigurationMap.cxx FGDeviceConfigurationMap.hxx \
+       FGButton.cxx FGButton.hxx \
+       FGMouseInput.cxx FGMouseInput.hxx \
+       FGKeyboardInput.cxx FGKeyboardInput.hxx \
+       FGJoystickInput.cxx FGJoystickInput.hxx 
 
 bin_PROGRAMS = js_demo fgjs
 
index 6fc8bc9973bde718d876360c90594c131857f7a2..6a36f9d806f0d87cb75d7a4fe661a60cc300cac7 100644 (file)
@@ -1,8 +1,10 @@
 // input.cxx -- handle user input from various sources.
 //
 // Written by David Megginson, started May 2001.
+// Major redesign by Torsten Dreyer, started August 2009
 //
 // Copyright (C) 2001 David Megginson, david@megginson.com
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
 #  include <config.h>
 #endif
 
-#include <simgear/compiler.h>
-
-#include <math.h>
-#include <ctype.h>
-#include <sstream>
-
-#include <fstream>
-#include <string>
-#include <vector>
-
-#include <simgear/compiler.h>
-
-#include <simgear/constants.h>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/math/SGMath.hxx>
-#include <simgear/props/props.hxx>
-#include <simgear/scene/util/SGSceneUserData.hxx>
-
-#include <Aircraft/aircraft.hxx>
-#include <Autopilot/xmlauto.hxx>
-#include <Cockpit/hud.hxx>
-#include <Cockpit/panel.hxx>
-#include <Cockpit/panel_io.hxx>
-#include <GUI/gui.h>
-#include <Model/panelnode.hxx>
-#include <Scripting/NasalSys.hxx>
-
-#include <Main/globals.hxx>
-#include <Main/fg_props.hxx>
-
 #include "input.hxx"
+#include "FGMouseInput.hxx"
+#include "FGKeyboardInput.hxx"
+#include "FGJoystickInput.hxx"
 
-#include <Scenery/scenery.hxx>
-#include <Main/renderer.hxx>
-
-using std::ifstream;
-using std::ostringstream;
-using std::string;
-using std::vector;
-
-void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
-void mouseMotionHandler(int x, int y);
-void keyHandler(int key, int keymod, int mousex, int mousey);
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Local variables.
-////////////////////////////////////////////////////////////////////////
-
-static FGInput * default_input = 0;
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Local functions.
-////////////////////////////////////////////////////////////////////////
-
-static int
-getModifiers ()
-{
-  return fgGetKeyModifiers() >> 1;
-}
-
-static bool
-getModShift ()
-{
-  return (fgGetKeyModifiers() & KEYMOD_SHIFT) != 0;
-}
-
-static bool
-getModCtrl ()
-{
-  return (fgGetKeyModifiers() & KEYMOD_CTRL) != 0;
-}
-
-static bool
-getModAlt ()
-{
-  return (fgGetKeyModifiers() & KEYMOD_ALT) != 0;
-}
-
-static bool
-getModMeta ()
-{
-  return (fgGetKeyModifiers() & KEYMOD_META) != 0;
-}
-
-static bool
-getModSuper ()
-{
-  return (fgGetKeyModifiers() & KEYMOD_SUPER) != 0;
-}
-
-static bool
-getModHyper ()
-{
-  return (fgGetKeyModifiers() & KEYMOD_HYPER) != 0;
-}
+#ifdef WITH_EVENTINPUT
+#include "FGLinuxEventInput.hxx"
+#endif
 
-\f
 ////////////////////////////////////////////////////////////////////////
 // Implementation of FGInput.
 ////////////////////////////////////////////////////////////////////////
 
 
-FGInput::FGInput () :
-    _key_event(fgGetNode("/devices/status/keyboard/event", true))
+FGInput::FGInput ()
 {
-    if (default_input == 0)
-        default_input = this;
+  set_subsystem( "input-mouse", new FGMouseInput() );
+  set_subsystem( "input-keyboard", new FGKeyboardInput() );
+  set_subsystem( "input-joystick", new FGJoystickInput() );
 }
 
 FGInput::~FGInput ()
 {
-    if (default_input == this)
-        default_input = 0;
-}
-
-void
-FGInput::init ()
-{
-  _init_joystick();
-  _init_mouse();
-
-  fgRegisterKeyHandler(keyHandler);
-  fgRegisterMouseClickHandler(mouseClickHandler);
-  fgRegisterMouseMotionHandler(mouseMotionHandler);
-}
-
-void
-FGInput::reinit ()
-{
-    init();
-}
-
-void
-FGInput::postinit ()
-{
-  _postinit_joystick();
-  _postinit_keyboard();
-}
-
-void
-FGInput::bind ()
-{
-  fgTie("/devices/status/keyboard", getModifiers);
-  fgTie("/devices/status/keyboard/shift", getModShift);
-  fgTie("/devices/status/keyboard/ctrl", getModCtrl);
-  fgTie("/devices/status/keyboard/alt", getModAlt);
-  fgTie("/devices/status/keyboard/meta", getModMeta);
-  fgTie("/devices/status/keyboard/super", getModSuper);
-  fgTie("/devices/status/keyboard/hyper", getModHyper);
-
-  _key_event->tie("key", SGRawValuePointer<int>(&_key_code));
-  _key_event->tie("pressed", SGRawValuePointer<bool>(&_key_pressed));
-  _key_event->tie("modifier", SGRawValuePointer<int>(&_key_modifiers));
-  _key_event->tie("modifier/shift", SGRawValuePointer<bool>(&_key_shift));
-  _key_event->tie("modifier/ctrl", SGRawValuePointer<bool>(&_key_ctrl));
-  _key_event->tie("modifier/alt", SGRawValuePointer<bool>(&_key_alt));
-  _key_event->tie("modifier/meta", SGRawValuePointer<bool>(&_key_meta));
-  _key_event->tie("modifier/super", SGRawValuePointer<bool>(&_key_super));
-  _key_event->tie("modifier/hyper", SGRawValuePointer<bool>(&_key_hyper));
-}
-
-void
-FGInput::unbind ()
-{
-  fgUntie("/devices/status/keyboard");
-  fgUntie("/devices/status/keyboard/shift");
-  fgUntie("/devices/status/keyboard/ctrl");
-  fgUntie("/devices/status/keyboard/alt");
-  fgUntie("/devices/status/keyboard/meta");
-  fgUntie("/devices/status/keyboard/super");
-  fgUntie("/devices/status/keyboard/hyper");
-
-  _key_event->untie("key");
-  _key_event->untie("pressed");
-  _key_event->untie("modifier");
-  _key_event->untie("modifier/shift");
-  _key_event->untie("modifier/ctrl");
-  _key_event->untie("modifier/alt");
-  _key_event->untie("modifier/meta");
-  _key_event->untie("modifier/super");
-  _key_event->untie("modifier/hyper");
-}
-
-void 
-FGInput::update (double dt)
-{
-  _update_keyboard();
-  _update_joystick(dt);
-  _update_mouse(dt);
-}
-
-void
-FGInput::suspend ()
-{
-    // NO-OP
-}
-
-void
-FGInput::resume ()
-{
-    // NO-OP
-}
-
-bool
-FGInput::is_suspended () const
-{
-    return false;
-}
-
-void
-FGInput::makeDefault (bool status)
-{
-    if (status)
-        default_input = this;
-    else if (default_input == this)
-        default_input = 0;
-}
-
-void
-FGInput::doKey (int k, int modifiers, int x, int y)
-{
-                                // Sanity check.
-  if (k < 0 || k >= MAX_KEYS) {
-    SG_LOG(SG_INPUT, SG_WARN, "Key value " << k << " out of range");
-    return;
-  }
-
-  _key_code = k;
-  _key_modifiers = modifiers >> 1;
-  _key_pressed = (modifiers & KEYMOD_RELEASED) == 0;
-  _key_shift = getModShift();
-  _key_ctrl = getModCtrl();
-  _key_alt = getModAlt();
-  _key_meta = getModMeta();
-  _key_super = getModSuper();
-  _key_hyper = getModHyper();
-  _key_event->fireValueChanged();
-  if (_key_code < 0)
-    return;
-
-  k = _key_code;
-  modifiers = _key_modifiers << 1;
-  if (!_key_pressed)
-      modifiers |= KEYMOD_RELEASED;
-  button &b = _key_bindings[k];
-
-                                // Key pressed.
-  if (!(modifiers & KEYMOD_RELEASED)) {
-    SG_LOG( SG_INPUT, SG_DEBUG, "User pressed key " << k
-            << " with modifiers " << modifiers );
-    if (!b.last_state || b.is_repeatable) {
-      const binding_list_t &bindings = _find_key_bindings(k, modifiers);
-
-      for (unsigned int i = 0; i < bindings.size(); i++)
-        bindings[i]->fire();
-      b.last_state = 1;
-    }
-  }
-                                // Key released.
-  else {
-    SG_LOG(SG_INPUT, SG_DEBUG, "User released key " << k
-           << " with modifiers " << modifiers);
-    if (b.last_state) {
-      const binding_list_t &bindings = _find_key_bindings(k, modifiers);
-      for (unsigned int i = 0; i < bindings.size(); i++)
-        bindings[i]->fire();
-      b.last_state = 0;
-    }
-  }
-}
-
-void
-FGInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
-{
-  int modifiers = fgGetKeyModifiers();
-
-  mouse &m = _mouse_bindings[0];
-  mouse_mode &mode = m.modes[m.current_mode];
-
-                                // Let the property manager know.
-  if (b >= 0 && b < MAX_MOUSE_BUTTONS)
-    m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
-
-                                // Pass on to PUI and the panel if
-                                // requested, and return if one of
-                                // them consumes the event.
-
-  if (updown != MOUSE_BUTTON_DOWN) {
-    // Execute the mouse up event in any case, may be we should
-    // stop processing here?
-    while (!_activePickCallbacks[b].empty()) {
-      _activePickCallbacks[b].front()->buttonReleased();
-      _activePickCallbacks[b].pop_front();
-    }
-  }
-
-  if (mode.pass_through) {
-    if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
-      return;
-    else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
-             globals->get_current_panel()->getVisibility() &&
-             globals->get_current_panel()->doMouseAction(b, updown, x, y))
-      return;
-    else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
-      return;
-    else {
-      // pui didn't want the click event so compute a
-      // scenegraph intersection point corresponding to the mouse click
-      if (updown == MOUSE_BUTTON_DOWN) {
-
-        // Get the list of hit callbacks. Take the first callback that
-        // accepts the mouse button press and ignore the rest of them
-        // That is they get sorted by distance and by scenegraph depth.
-        // The nearest one is the first one and the deepest
-        // (the most specialized one in the scenegraph) is the first.
-        std::vector<SGSceneryPick> pickList;
-        if (FGRenderer::pick(pickList, ea)) {
-          std::vector<SGSceneryPick>::const_iterator i;
-          for (i = pickList.begin(); i != pickList.end(); ++i) {
-            if (i->callback->buttonPressed(b, i->info)) {
-              _activePickCallbacks[b].push_back(i->callback);
-              return;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // OK, PUI and the panel didn't want the click
-  if (b >= MAX_MOUSE_BUTTONS) {
-    SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
-           << " where only " << MAX_MOUSE_BUTTONS << " expected");
-    return;
-  }
-
-  _update_button(m.modes[m.current_mode].buttons[b], modifiers, 0 != updown, x, y);
-}
-
-void
-FGInput::doMouseMotion (int x, int y)
-{
-  // Don't call fgGetKeyModifiers() here, until we are using a
-  // toolkit that supports getting the mods from outside a key
-  // callback.  Glut doesn't.
-  int modifiers = KEYMOD_NONE;
-
-  int xsize = fgGetInt("/sim/startup/xsize", 800);
-  int ysize = fgGetInt("/sim/startup/ysize", 600);
-
-  mouse &m = _mouse_bindings[0];
-
-  if (m.current_mode < 0 || m.current_mode >= m.nModes) {
-      m.x = x;
-      m.y = y;
-      return;
-  }
-  mouse_mode &mode = m.modes[m.current_mode];
-
-                                // Pass on to PUI if requested, and return
-                                // if PUI consumed the event.
-  if (mode.pass_through && puMouse(x, y)) {
-      m.x = x;
-      m.y = y;
-      return;
-  }
-
-                                // OK, PUI didn't want the event,
-                                // so we can play with it.
-  if (x != m.x) {
-    int delta = x - m.x;
-    for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
-      mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
-  }
-  if (y != m.y) {
-    int delta = y - m.y;
-    for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
-      mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
-  }
-
-                                // Constrain the mouse if requested
-  if (mode.constrained) {
-    bool need_warp = false;
-    if (x <= (xsize * .25) || x >= (xsize * .75)) {
-      x = int(xsize * .5);
-      need_warp = true;
-    }
-
-    if (y <= (ysize * .25) || y >= (ysize * .75)) {
-      y = int(ysize * .5);
-      need_warp = true;
-    }
-
-    if (need_warp)
-      fgWarpMouse(x, y);
-  }
-
-  if (m.x != x)
-      fgSetInt("/devices/status/mice/mouse/x", m.x = x);
-
-  if (m.y != y)
-      fgSetInt("/devices/status/mice/mouse/y", m.y = y);
-}
-
-
-void
-FGInput::_scan_joystick_dir(SGPath *path, SGPropertyNode* node, int *index)
-{
-  ulDir *dir = ulOpenDir(path->c_str());
-  if (dir) {
-    ulDirEnt* dent;
-    while ((dent = ulReadDir(dir)) != 0) {
-      if (dent->d_name[0] == '.')
-        continue;
-
-      SGPath p(path->str());
-      p.append(dent->d_name);
-      _scan_joystick_dir(&p, node, index);
-    }
-    ulCloseDir(dir);
-
-  } else if (path->extension() == "xml") {
-    SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path->str());
-    SGPropertyNode *n = node->getChild("js-named", (*index)++, true);
-    readProperties(path->str(), n);
-    n->setStringValue("source", path->c_str());
-  }
-}
-
-
-void
-FGInput::_init_joystick ()
-{
-  jsInit();
-                                // TODO: zero the old bindings first.
-  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
-  SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
-
-  // read all joystick xml files into /input/joysticks/js_named[1000++]
-  SGPath path(globals->get_fg_root());
-  path.append("Input/Joysticks");
-  int js_named_index = 1000;
-  _scan_joystick_dir(&path, js_nodes, &js_named_index);
-
-  // build name->node map with each <name> (reverse order)
-  map<string, SGPropertyNode_ptr> jsmap;
-  vector<SGPropertyNode_ptr> js_named = js_nodes->getChildren("js-named");
-
-  for (int k = (int)js_named.size() - 1; k >= 0; k--) {
-    SGPropertyNode *n = js_named[k];
-    vector<SGPropertyNode_ptr> names = n->getChildren("name");
-    if (names.size() && (n->getChildren("axis").size() || n->getChildren("button").size()))
-      for (unsigned int j = 0; j < names.size(); j++)
-        jsmap[names[j]->getStringValue()] = n;
-  }
-
-  // set up js[] nodes
-  for (int i = 0; i < MAX_JOYSTICKS; i++) {
-    jsJoystick * js = new jsJoystick(i);
-    _joystick_bindings[i].js = js;
-
-    if (js->notWorking()) {
-      SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
-      continue;
-    }
-
-    const char * name = js->getName();
-    SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
-
-    if (js_node) {
-      SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
-
-    } else {
-      SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
-      SGPropertyNode_ptr named;
-
-      if ((named = jsmap[name])) {
-        string source = named->getStringValue("source", "user defined");
-        SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
-
-      } else if ((named = jsmap["default"])) {
-        string source = named->getStringValue("source", "user defined");
-        SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
-            << "\"\nUsing default: \"" << source << '"');
-
-      } else {
-        throw sg_exception(string("No joystick configuration file with <name>")
-                           + name + "</name> entry found!");
-      }
-
-      js_node = js_nodes->getChild("js", i, true);
-      copyProperties(named, js_node);
-      js_node->setStringValue("id", name);
-    }
-  }
-
-  // get rid of unused config nodes
-  js_nodes->removeChildren("js-named", false);
-}
-
-
-void
-FGInput::_postinit_keyboard()
-{
-  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
-  _module = "__kbd";
-  SGPropertyNode * key_nodes = fgGetNode("/input/keyboard");
-  if (key_nodes == 0) {
-    SG_LOG(SG_INPUT, SG_WARN, "No key bindings (/input/keyboard)!!");
-    key_nodes = fgGetNode("/input/keyboard", true);
-  }
-
-  FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
-  vector<SGPropertyNode_ptr> nasal = key_nodes->getChildren("nasal");
-  for (unsigned int j = 0; j < nasal.size(); j++) {
-    nasal[j]->setStringValue("module", _module.c_str());
-    nasalsys->handleCommand(nasal[j]);
-  }
-
-  vector<SGPropertyNode_ptr> keys = key_nodes->getChildren("key");
-  for (unsigned int i = 0; i < keys.size(); i++) {
-    int index = keys[i]->getIndex();
-    SG_LOG(SG_INPUT, SG_DEBUG, "Binding key " << index);
-
-    _key_bindings[index].bindings->clear();
-    _key_bindings[index].is_repeatable = keys[i]->getBoolValue("repeatable");
-    _key_bindings[index].last_state = 0;
-    _read_bindings(keys[i], _key_bindings[index].bindings, KEYMOD_NONE);
-  }
-}
-
-
-void
-FGInput::_postinit_joystick()
-{
-  FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
-  SGPropertyNode *js_nodes = fgGetNode("/input/joysticks");
-
-  for (int i = 0; i < MAX_JOYSTICKS; i++) {
-    SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
-    jsJoystick *js = _joystick_bindings[i].js;
-    if (!js_node || js->notWorking())
-      continue;
-
-#ifdef WIN32
-    JOYCAPS jsCaps ;
-    joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
-    unsigned int nbuttons = jsCaps.wNumButtons;
-    if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
-#else
-    unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
-#endif
-
-    int naxes = js->getNumAxes();
-    if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
-    _joystick_bindings[i].naxes = naxes;
-    _joystick_bindings[i].nbuttons = nbuttons;
-
-    SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
-
-                                // Set up range arrays
-    float minRange[MAX_JOYSTICK_AXES];
-    float maxRange[MAX_JOYSTICK_AXES];
-    float center[MAX_JOYSTICK_AXES];
-
-                                // Initialize with default values
-    js->getMinRange(minRange);
-    js->getMaxRange(maxRange);
-    js->getCenter(center);
-
-                                // Allocate axes and buttons
-    _joystick_bindings[i].axes = new axis[naxes];
-    _joystick_bindings[i].buttons = new button[nbuttons];
-
-    //
-    // Initialize nasal groups.
-    //
-    ostringstream str;
-    str << "__js" << i;
-    _module = str.str();
-    nasalsys->createModule(_module.c_str(), _module.c_str(), "", 0);
-
-    vector<SGPropertyNode_ptr> nasal = js_node->getChildren("nasal");
-    unsigned int j;
-    for (j = 0; j < nasal.size(); j++) {
-      nasal[j]->setStringValue("module", _module.c_str());
-      nasalsys->handleCommand(nasal[j]);
-    }
-
-    //
-    // Initialize the axes.
-    //
-    vector<SGPropertyNode_ptr> axes = js_node->getChildren("axis");
-    size_t nb_axes = axes.size();
-    for (j = 0; j < nb_axes; j++ ) {
-      const SGPropertyNode * axis_node = axes[j];
-      const SGPropertyNode * num_node = axis_node->getChild("number");
-      int n_axis = axis_node->getIndex();
-      if (num_node != 0) {
-          n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
-
-          // Silently ignore platforms that are not specified within the
-          // <number></number> section
-          if (n_axis < 0)
-             continue;
-      }
-
-      if (n_axis >= naxes) {
-          SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
-          continue;
-      }
-      axis &a = _joystick_bindings[i].axes[n_axis];
-
-      js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
-
-      a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
-      minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
-      maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
-      center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
-
-      _read_bindings(axis_node, a.bindings, KEYMOD_NONE);
-
-      // Initialize the virtual axis buttons.
-      _init_button(axis_node->getChild("low"), a.low, "low");
-      a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
-
-      _init_button(axis_node->getChild("high"), a.high, "high");
-      a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
-      a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
-      a.last_dt = 0.0;
-    }
-
-    //
-    // Initialize the buttons.
-    //
-    vector<SGPropertyNode_ptr> buttons = js_node->getChildren("button");
-    char buf[32];
-    for (j = 0; j < buttons.size() && j < nbuttons; j++) {
-      const SGPropertyNode * button_node = buttons[j];
-      const SGPropertyNode * num_node = button_node->getChild("number");
-      size_t n_but = button_node->getIndex();
-      if (num_node != 0) {
-          n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
-      }
-
-      if (n_but >= nbuttons) {
-          SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
-          continue;
-      }
-
-      sprintf(buf, "%d", n_but);
-      SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << n_but);
-      _init_button(button_node,
-                   _joystick_bindings[i].buttons[n_but],
-                   buf);
-
-      // get interval-sec property
-      button &b = _joystick_bindings[i].buttons[n_but];
-      if (button_node != 0) {
-        b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
-        b.last_dt = 0.0;
-      }
-    }
-
-    js->setMinRange(minRange);
-    js->setMaxRange(maxRange);
-    js->setCenter(center);
-  }
-}
-
-
-// 
-// Map of all known cursor names
-// This used to contain all the Glut cursors, but those are
-// not defined by other toolkits.  It now supports only the cursor
-// images we actually use, in the interest of portability.  Someday,
-// it would be cool to write an OpenGL cursor renderer, with the
-// cursors defined as textures referenced in the property tree.  This
-// list could then be eliminated. -Andy
-//
-static struct {
-  const char * name;
-  int cursor;
-} mouse_cursor_map[] = {
-  { "none", MOUSE_CURSOR_NONE },
-  { "inherit", MOUSE_CURSOR_POINTER },
-  { "wait", MOUSE_CURSOR_WAIT },
-  { "crosshair", MOUSE_CURSOR_CROSSHAIR },
-  { "left-right", MOUSE_CURSOR_LEFTRIGHT },
-  { 0, 0 }
-};
-
-void
-FGInput::_init_mouse ()
-{
-  SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
-  _module = "";
-
-  SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
-  if (mouse_nodes == 0) {
-    SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
-    mouse_nodes = fgGetNode("/input/mice", true);
-  }
-
-  int j;
-  for (int i = 0; i < MAX_MICE; i++) {
-    SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
-    mouse &m = _mouse_bindings[i];
-
-                                // Grab node pointers
-    char buf[64];
-    sprintf(buf, "/devices/status/mice/mouse[%d]/mode", i);
-    m.mode_node = fgGetNode(buf);
-    if (m.mode_node == NULL) {
-      m.mode_node = fgGetNode(buf, true);
-      m.mode_node->setIntValue(0);
-    }
-    for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
-      sprintf(buf, "/devices/status/mice/mouse[%d]/button[%d]", i, j);
-      m.mouse_button_nodes[j] = fgGetNode(buf, true);
-      m.mouse_button_nodes[j]->setBoolValue(false);
-    }
-
-                                // Read all the modes
-    m.nModes = mouse_node->getIntValue("mode-count", 1);
-    m.modes = new mouse_mode[m.nModes];
-
-    for (int j = 0; j < m.nModes; j++) {
-      int k;
-
-                                // Read the mouse cursor for this mode
-      SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
-      const char * cursor_name =
-        mode_node->getStringValue("cursor", "inherit");
-      m.modes[j].cursor = MOUSE_CURSOR_POINTER;
-      for (k = 0; mouse_cursor_map[k].name != 0; k++) {
-        if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
-          m.modes[j].cursor = mouse_cursor_map[k].cursor;
-          break;
-        }
-      }
-
-                                // Read other properties for this mode
-      m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
-      m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
-
-                                // Read the button bindings for this mode
-      m.modes[j].buttons = new button[MAX_MOUSE_BUTTONS];
-      char buf[32];
-      for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
-        sprintf(buf, "mouse button %d", k);
-        SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
-        _init_button(mode_node->getChild("button", k),
-                     m.modes[j].buttons[k],
-                     buf);
-      }
-
-                                // Read the axis bindings for this mode
-      _read_bindings(mode_node->getChild("x-axis", 0, true),
-                     m.modes[j].x_bindings,
-                     KEYMOD_NONE);
-      _read_bindings(mode_node->getChild("y-axis", 0, true),
-                     m.modes[j].y_bindings,
-                     KEYMOD_NONE);
-    }
-  }
-}
-
-
-void
-FGInput::_init_button (const SGPropertyNode * node,
-                       button &b,
-                       const string name)
-{       
-  if (node == 0) {
-    SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for button " << name);
-  } else {
-    b.is_repeatable = node->getBoolValue("repeatable", b.is_repeatable);
-    
-                // Get the bindings for the button
-    _read_bindings(node, b.bindings, KEYMOD_NONE);
-  }
-}
-
-
-void
-FGInput::_update_keyboard ()
-{
-  // no-op
-}
-
-
-void
-FGInput::_update_joystick (double dt)
-{
-  float axis_values[MAX_JOYSTICK_AXES];
-  int modifiers = fgGetKeyModifiers();
-  int buttons;
-
-  for (int i = 0; i < MAX_JOYSTICKS; i++) {
-
-    jsJoystick * js = _joystick_bindings[i].js;
-    if (js == 0 || js->notWorking())
-      continue;
-
-    js->read(&buttons, axis_values);
-
-                                // Fire bindings for the axes.
-    for (int j = 0; j < _joystick_bindings[i].naxes; j++) {
-      axis &a = _joystick_bindings[i].axes[j];
-
-                                // Do nothing if the axis position
-                                // is unchanged; only a change in
-                                // position fires the bindings.
-      if (fabs(axis_values[j] - a.last_value) > a.tolerance) {
-        a.last_value = axis_values[j];
-        for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
-          a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
-      }
-
-                                // do we have to emulate axis buttons?
-      a.last_dt += dt;
-      if(a.last_dt >= a.interval_sec) {
-        if (a.low.bindings[modifiers].size())
-          _update_button(_joystick_bindings[i].axes[j].low,
-                         modifiers,
-                         axis_values[j] < a.low_threshold,
-                         -1, -1);
-
-        if (a.high.bindings[modifiers].size())
-          _update_button(_joystick_bindings[i].axes[j].high,
-                         modifiers,
-                         axis_values[j] > a.high_threshold,
-                         -1, -1);
-         a.last_dt -= a.interval_sec;
-      }
-    }
-
-                                // Fire bindings for the buttons.
-    for (int j = 0; j < _joystick_bindings[i].nbuttons; j++) {
-      button &b = _joystick_bindings[i].buttons[j];
-      b.last_dt += dt;
-      if(b.last_dt >= b.interval_sec) {
-        _update_button(_joystick_bindings[i].buttons[j],
-                       modifiers,
-                       (buttons & (1 << j)) > 0,
-                       -1, -1);
-        b.last_dt -= b.interval_sec;
-      }
-    }
-  }
-}
-
-void
-FGInput::_update_mouse ( double dt )
-{
-  mouse &m = _mouse_bindings[0];
-  int mode =  m.mode_node->getIntValue();
-  if (mode != m.current_mode) {
-    m.current_mode = mode;
-    m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
-    if (mode >= 0 && mode < m.nModes) {
-      fgSetMouseCursor(m.modes[mode].cursor);
-      m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
-      m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
-      fgWarpMouse(m.x, m.y);
-    } else {
-      SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
-      fgSetMouseCursor(MOUSE_CURSOR_POINTER);
-    }
-  }
-
-  if ( fgGetBool( "/sim/mouse/hide-cursor", true ) ) {
-      if ( m.x != m.save_x || m.y != m.save_y ) {
-          m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
-          if (fgGetMouseCursor() == MOUSE_CURSOR_NONE)
-              fgSetMouseCursor(m.modes[mode].cursor);
-      } else {
-          m.timeout -= dt;
-          if ( m.timeout <= 0.0 ) {
-              fgSetMouseCursor(MOUSE_CURSOR_NONE);
-              m.timeout = 0.0;
-          }
-      }
-      m.save_x = m.x;
-      m.save_y = m.y;
-  }
-
-  // handle repeatable mouse press events
-  std::map<int, std::list<SGSharedPtr<SGPickCallback> > >::iterator mi;
-  for (mi = _activePickCallbacks.begin();
-       mi != _activePickCallbacks.end(); ++mi) {
-    std::list<SGSharedPtr<SGPickCallback> >::iterator li;
-    for (li = mi->second.begin(); li != mi->second.end(); ++li) {
-      (*li)->update(dt);
-    }
-  }
-}
-
-void
-FGInput::_update_button (button &b, int modifiers, bool pressed,
-                         int x, int y)
-{
-  if (pressed) {
-                                // The press event may be repeated.
-    if (!b.last_state || b.is_repeatable) {
-      SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
-      for (unsigned int k = 0; k < b.bindings[modifiers].size(); k++) {
-        b.bindings[modifiers][k]->fire(x, y);
-      }
-    }
-  } else {
-                                // The release event is never repeated.
-    if (b.last_state) {
-      SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" );
-      for (unsigned int k = 0; k < b.bindings[modifiers|KEYMOD_RELEASED].size(); k++)
-        b.bindings[modifiers|KEYMOD_RELEASED][k]->fire(x, y);
-    }
-  }
-          
-  b.last_state = pressed;
-}  
-
-
-void
-FGInput::_read_bindings (const SGPropertyNode * node, 
-                         binding_list_t * binding_list,
-                         int modifiers)
-{
-  SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
-  vector<SGPropertyNode_ptr> bindings = node->getChildren("binding");
-  for (unsigned int i = 0; i < bindings.size(); i++) {
-    const char *cmd = bindings[i]->getStringValue("command");
-    SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding " << cmd);
-
-    if (!strcmp(cmd, "nasal") && !_module.empty())
-      bindings[i]->setStringValue("module", _module.c_str());
-    binding_list[modifiers].push_back(new SGBinding(bindings[i], globals->get_props()));
-  }
-
-                                // Read nested bindings for modifiers
-  if (node->getChild("mod-up") != 0)
-    _read_bindings(node->getChild("mod-up"), binding_list,
-                   modifiers|KEYMOD_RELEASED);
-
-  if (node->getChild("mod-shift") != 0)
-    _read_bindings(node->getChild("mod-shift"), binding_list,
-                   modifiers|KEYMOD_SHIFT);
-
-  if (node->getChild("mod-ctrl") != 0)
-    _read_bindings(node->getChild("mod-ctrl"), binding_list,
-                   modifiers|KEYMOD_CTRL);
-
-  if (node->getChild("mod-alt") != 0)
-    _read_bindings(node->getChild("mod-alt"), binding_list,
-                   modifiers|KEYMOD_ALT);
-
-  if (node->getChild("mod-meta") != 0)
-    _read_bindings(node->getChild("mod-meta"), binding_list,
-                   modifiers|KEYMOD_META);
-
-  if (node->getChild("mod-super") != 0)
-    _read_bindings(node->getChild("mod-super"), binding_list,
-                   modifiers|KEYMOD_SUPER);
-
-  if (node->getChild("mod-hyper") != 0)
-    _read_bindings(node->getChild("mod-hyper"), binding_list,
-                   modifiers|KEYMOD_HYPER);
-}
-
-
-const FGInput::binding_list_t&
-FGInput::_find_key_bindings (unsigned int k, int modifiers)
-{
-  unsigned char kc = (unsigned char)k;
-  button &b = _key_bindings[k];
-
-                                // Try it straight, first.
-  if (b.bindings[modifiers].size() > 0)
-    return b.bindings[modifiers];
-
-                                // Alt-Gr is CTRL+ALT
-  else if (modifiers&(KEYMOD_CTRL|KEYMOD_ALT))
-    return _find_key_bindings(k, modifiers&~(KEYMOD_CTRL|KEYMOD_ALT));
-
-                                // Try removing the control modifier
-                                // for control keys.
-  else if ((modifiers&KEYMOD_CTRL) && iscntrl(kc))
-    return _find_key_bindings(k, modifiers&~KEYMOD_CTRL);
-
-                                // Try removing shift modifier 
-                                // for upper case or any punctuation
-                                // (since different keyboards will
-                                // shift different punctuation types)
-  else if ((modifiers&KEYMOD_SHIFT) && (isupper(kc) || ispunct(kc)))
-    return _find_key_bindings(k, modifiers&~KEYMOD_SHIFT);
-
-                                // Try removing alt modifier for
-                                // high-bit characters.
-  else if ((modifiers&KEYMOD_ALT) && k >= 128 && k < 256)
-    return _find_key_bindings(k, modifiers&~KEYMOD_ALT);
-
-                                // Give up and return the empty vector.
-  else
-    return b.bindings[modifiers];
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGInput::button.
-////////////////////////////////////////////////////////////////////////
-
-FGInput::button::button ()
-  : is_repeatable(false),
-    interval_sec(0),
-    last_dt(0),
-    last_state(0)
-{
-}
-
-FGInput::button::~button ()
-{
-                                // FIXME: memory leak
-//   for (int i = 0; i < KEYMOD_MAX; i++)
-//     for (int j = 0; i < bindings[i].size(); j++)
-//       delete bindings[i][j];
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGInput::axis.
-////////////////////////////////////////////////////////////////////////
-
-FGInput::axis::axis ()
-  : last_value(9999999),
-    tolerance(0.002),
-    low_threshold(-0.9),
-    high_threshold(0.9),
-    interval_sec(0),
-    last_dt(0)
-{
-}
-
-FGInput::axis::~axis ()
-{
-//   for (int i = 0; i < KEYMOD_MAX; i++)
-//     for (int j = 0; i < bindings[i].size(); j++)
-//       delete bindings[i][j];
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGInput::joystick.
-////////////////////////////////////////////////////////////////////////
-
-FGInput::joystick::joystick ()
-  : jsnum(0),
-    js(0),
-    naxes(0),
-    nbuttons(0),
-    axes(0),
-    buttons(0)
-{
+  // SGSubsystemGroup deletes all subsystem in it's destructor
 }
 
-FGInput::joystick::~joystick ()
-{
-//   delete js;
-  delete[] axes;
-  delete[] buttons;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGInput::mouse_mode
-////////////////////////////////////////////////////////////////////////
-
-FGInput::mouse_mode::mouse_mode ()
-  : cursor(MOUSE_CURSOR_POINTER),
-    constrained(false),
-    pass_through(false),
-    buttons(0)
-{
-}
-
-FGInput::mouse_mode::~mouse_mode ()
-{
-                                // FIXME: memory leak
-//   for (int i = 0; i < KEYMOD_MAX; i++) {
-//     int j;
-//     for (j = 0; i < x_bindings[i].size(); j++)
-//       delete bindings[i][j];
-//     for (j = 0; j < y_bindings[i].size(); j++)
-//       delete bindings[i][j];
-//   }
-  delete [] buttons;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of FGInput::mouse
-////////////////////////////////////////////////////////////////////////
-
-FGInput::mouse::mouse ()
-  : x(-1),
-    y(-1),
-    save_x(-1),
-    save_y(-1),
-    nModes(1),
-    current_mode(0),
-    modes(0)
-{
-}
-
-FGInput::mouse::~mouse ()
-{
-  delete [] modes;
-}
-
-////////////////////////////////////////////////////////////////////////
-// Implementation of OS callbacks.
-////////////////////////////////////////////////////////////////////////
-
-void keyHandler(int key, int keymod, int mousex, int mousey)
-{
-    if((keymod & KEYMOD_RELEASED) == 0)
-        if(puKeyboard(key, PU_DOWN))
-            return;
-
-    if(default_input)
-        default_input->doKey(key, keymod, mousex, mousey);
-}
-
-void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
-{
-    if(default_input)
-      default_input->doMouseClick(button, updown, x, y, mainWindow, ea);
-}
-
-void mouseMotionHandler(int x, int y)
-{
-    if (default_input != 0)
-        default_input->doMouseMotion(x, y);
-}
index e71d8620e17ca37307784c2a9154ee4081c35177..f26f21a9333796adc6dba60521c8b45a8f6dc207 100644 (file)
@@ -1,8 +1,10 @@
 // input.hxx -- handle user input from various sources.
 //
 // Written by David Megginson, started May 2001.
+// Major redesign by Torsten Dreyer, started August 2009
 //
 // Copyright (C) 2001 David Megginson, david@megginson.com
+// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
 # error This library requires C++
 #endif
 
-#include <plib/js.h>
-#include <plib/ul.h>
-
-#include <simgear/compiler.h>
-
-#include <simgear/misc/sg_path.hxx>
 #include <simgear/structure/subsystem_mgr.hxx>
-#include <simgear/structure/SGBinding.hxx>
-#include <simgear/props/condition.hxx>
-#include <simgear/props/props.hxx>
-#include <simgear/scene/util/SGSceneUserData.hxx>
-
-#include <Main/fg_os.hxx>
-#include <Main/fg_props.hxx>
-#include <Main/globals.hxx>
-
-#include <map>
-#include <list>
-#include <vector>
-
-using std::map;
-using std::vector;
-
 
 \f
 
-#if defined( UL_WIN32 )
-#define TGT_PLATFORM   "windows"
-#elif defined ( UL_MAC_OSX )
-#define TGT_PLATFORM    "mac"
-#else
-#define TGT_PLATFORM   "unix"
-#endif
-
 
 \f
 ////////////////////////////////////////////////////////////////////////
@@ -76,7 +48,7 @@ using std::vector;
  * keyboard, joystick, mouse, or even panel switches -- in a consistent
  * way, and to allow users to rebind any of the actions at runtime.</p>
  */
-class FGInput : public SGSubsystem
+class FGInput : public SGSubsystemGroup
 {
 public:
   /**
@@ -89,259 +61,6 @@ public:
    */
   virtual ~FGInput();
 
-  //
-  // Implementation of SGSubsystem.
-  //
-  virtual void init ();
-  virtual void reinit ();
-  virtual void postinit ();
-  virtual void bind ();
-  virtual void unbind ();
-  virtual void update (double dt);
-  virtual void suspend ();
-  virtual void resume ();
-  virtual bool is_suspended () const;
-
-
-  /**
-   * Control whether this is the default module to receive events.
-   *
-   * The first input module created will set itself as the default
-   * automatically.
-   *
-   * @param status true if this should be the default module for
-   * events, false otherwise.
-   */
-  virtual void makeDefault (bool status = true);
-
-
-  /**
-   * Handle a single keystroke.
-   *
-   * @param k The integer key code, see Main/fg_os.hxx
-   * @param modifiers Modifier keys pressed (bitfield).
-   * @param x The mouse x position at the time of keypress.
-   * @param y The mouse y position at the time of keypress.
-   */
-  virtual void doKey (int k, int modifiers, int x, int y);
-
-
-  /**
-   * Handle a mouse click event.
-   *
-   * @param button The mouse button selected.
-   * @param updown Button status.
-   * @param x The X position of the mouse event, in screen coordinates.
-   * @param y The Y position of the mouse event, in screen coordinates.
-   */
-  virtual void doMouseClick (int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
-
-
-  /**
-   * Handle mouse motion.
-   *
-   * @param x The new mouse x position, in screen coordinates.
-   * @param y The new mouse y position, in screen coordinates.
-   */
-  virtual void doMouseMotion (int x, int y);
-
-
-private:
-                               // Constants
-  enum 
-  {
-    MAX_KEYS = 1024,
-    MAX_JOYSTICKS = 10,
-    MAX_JOYSTICK_AXES = _JS_MAX_AXES,
-    MAX_JOYSTICK_BUTTONS = 32,
-
-    MAX_MICE = 1,
-    MAX_MOUSE_BUTTONS = 8
-  };
-  struct mouse;
-  friend struct mouse;
-
-  typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
-
-  /**
-   * Settings for a key or button.
-   */
-  struct button {
-    button ();
-    virtual ~button ();
-    bool is_repeatable;
-    float interval_sec;
-    float last_dt;
-    int last_state;
-    binding_list_t bindings[KEYMOD_MAX];
-  };
-
-
-  /**
-   * Settings for a single joystick axis.
-   */
-  struct axis {
-    axis ();
-    virtual ~axis ();
-    float last_value;
-    float tolerance;
-    binding_list_t bindings[KEYMOD_MAX];
-    float low_threshold;
-    float high_threshold;
-    struct button low;
-    struct button high;
-    float interval_sec;
-    double last_dt;
-  };
-
-
-  /**
-   * Settings for a joystick.
-   */
-  struct joystick {
-    joystick ();
-    virtual ~joystick ();
-    int jsnum;
-    jsJoystick * js;
-    int naxes;
-    int nbuttons;
-    axis * axes;
-    button * buttons;
-  };
-
-
-  /**
-   * Settings for a mouse mode.
-   */
-  struct mouse_mode {
-    mouse_mode ();
-    virtual ~mouse_mode ();
-    int cursor;
-    bool constrained;
-    bool pass_through;
-    button * buttons;
-    binding_list_t x_bindings[KEYMOD_MAX];
-    binding_list_t y_bindings[KEYMOD_MAX];
-  };
-
-
-  /**
-   * Settings for a mouse.
-   */
-  struct mouse {
-    mouse ();
-    virtual ~mouse ();
-    int x;
-    int y;
-    int save_x;
-    int save_y;
-    SGPropertyNode_ptr mode_node;
-    SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
-    int nModes;
-    int current_mode;
-    double timeout;
-    mouse_mode * modes;
-  };
-
-
-  /**
-   * Initialize joystick bindings.
-   */
-  void _init_joystick ();
-
-
-  /**
-   * Scan directory recursively for "named joystick" configuration files,
-   * and read them into /input/joysticks/js-named[index]++.
-   */
-  void _scan_joystick_dir (SGPath *path, SGPropertyNode* node, int *index);
-
-
-  /**
-   * Initialize mouse bindings.
-   */
-  void _init_mouse ();
-
-
-  /**
-   * Initialize a single button.
-   */
-  inline void _init_button (const SGPropertyNode * node,
-                           button &b,
-                           const string name);
-
-  /**
-   * Initialize key bindings, as well as those joystick parts that
-   * depend on a working Nasal subsystem.
-   */
-  void _postinit_keyboard ();
-  void _postinit_joystick ();
-
-  /**
-   * Update the keyboard.
-   */
-  void _update_keyboard ();
-
-
-  /**
-   * Update the joystick.
-   */
-  void _update_joystick (double dt);
-
-
-  /**
-   * Update the mouse.
-   */
-  void _update_mouse (double dt);
-
-
-  /**
-   * Update a single button.
-   */
-  inline void _update_button (button &b, int modifiers, bool pressed,
-                             int x, int y);
-
-
-  /**
-   * Read bindings and modifiers.
-   */
-  void _read_bindings (const SGPropertyNode * node,
-                      binding_list_t * binding_list,
-                      int modifiers);
-
-  /**
-   * Look up the bindings for a key code.
-   */
-  const binding_list_t& _find_key_bindings (unsigned int k,
-                                            int modifiers);
-
-  button _key_bindings[MAX_KEYS];
-  joystick _joystick_bindings[MAX_JOYSTICKS];
-  mouse _mouse_bindings[MAX_MICE];
-
-  /**
-   * Nasal module name/namespace.
-   */
-  string _module;
-
-  /**
-   * List of currently pressed mouse button events
-   */
-  std::map<int, std::list<SGSharedPtr<SGPickCallback> > > _activePickCallbacks;
-
-  /**
-   * Key listener interface.
-   */
-  SGPropertyNode_ptr _key_event;
-  int  _key_code;
-  int  _key_modifiers;
-  bool _key_pressed;
-  bool _key_shift;
-  bool _key_ctrl;
-  bool _key_alt;
-  bool _key_meta;
-  bool _key_super;
-  bool _key_hyper;
 };
 
 #endif // _INPUT_HXX