--- /dev/null
+// 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;
+}
+
+
--- /dev/null
+// 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
--- /dev/null
+// 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);
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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());
+ }
+}
+
+
--- /dev/null
+// 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
--- /dev/null
+// 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() );
+ }
+}
--- /dev/null
+// 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
--- /dev/null
+// 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;
+ }
+ }
+ }
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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
--- /dev/null
+// 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);
+}
+
+
--- /dev/null
+// 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
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
// 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);
-}
// 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
////////////////////////////////////////////////////////////////////////
* 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:
/**
*/
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