diff --git a/src/Input/FGButton.cxx b/src/Input/FGButton.cxx new file mode 100644 index 000000000..ee5953aca --- /dev/null +++ b/src/Input/FGButton.cxx @@ -0,0 +1,75 @@ +// FGButton.cxx -- a simple button/key wrapper class +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGButton.hxx" + +FGButton::FGButton () + : is_repeatable(false), + interval_sec(0), + last_dt(0), + last_state(0) +{ +} + +FGButton::~FGButton () +{ + // bindings is a list of SGSharedPtr + // no cleanup required +} + + +void FGButton::init( const SGPropertyNode * node, const string name, string & module ) +{ + if (node == 0) { + SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for button " << name); + } else { + is_repeatable = node->getBoolValue("repeatable", is_repeatable); + // Get the bindings for the button + read_bindings( node, bindings, KEYMOD_NONE, module ); + } +} + +void FGButton::update( int modifiers, bool pressed, int x, int y) +{ + if (pressed) { + // The press event may be repeated. + if (!last_state || is_repeatable) { + SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" ); + for (unsigned int k = 0; k < bindings[modifiers].size(); k++) { + bindings[modifiers][k]->fire(x, y); + } + } + } else { + // The release event is never repeated. + if (last_state) { + SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" ); + for (unsigned int k = 0; k < bindings[modifiers|KEYMOD_RELEASED].size(); k++) + bindings[modifiers|KEYMOD_RELEASED][k]->fire(x, y); + } + } + + last_state = pressed; +} + + diff --git a/src/Input/FGButton.hxx b/src/Input/FGButton.hxx new file mode 100644 index 000000000..6ab5347a0 --- /dev/null +++ b/src/Input/FGButton.hxx @@ -0,0 +1,44 @@ +// FGButton.hxx -- a simple button/key wrapper class +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef FGBUTTON_H +#define FGBUTTON_H + +#include "FGCommonInput.hxx" +#include
+ +class FGButton : public FGCommonInput { +public: + FGButton(); + virtual ~FGButton(); + void init( const SGPropertyNode * node, const string name, string & module ); + void update( int modifiers, bool pressed, int x = -1, int y = -1); + bool is_repeatable; + float interval_sec; + float last_dt; + int last_state; + binding_list_t bindings[KEYMOD_MAX]; +}; + +#endif diff --git a/src/Input/FGCommonInput.cxx b/src/Input/FGCommonInput.cxx new file mode 100644 index 000000000..513e9a640 --- /dev/null +++ b/src/Input/FGCommonInput.cxx @@ -0,0 +1,71 @@ +// FGCommonInput.cxx -- common functions for all Input subsystems +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGCommonInput.hxx" +#include
+#include
+ +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 bindings = node->getChildren("binding"); + string nasal = "nasal"; + for (unsigned int i = 0; i < bindings.size(); i++) { + const char *cmd = bindings[i]->getStringValue("command"); + SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding " << cmd); + if (nasal.compare(cmd) == 0 && !module.empty()) + bindings[i]->setStringValue("module", module.c_str()); + binding_list[modifiers].push_back(new SGBinding(bindings[i], globals->get_props())); + } + + // Read nested bindings for modifiers + if (node->getChild("mod-up") != 0) + read_bindings(node->getChild("mod-up"), binding_list, + modifiers|KEYMOD_RELEASED, module); + + if (node->getChild("mod-shift") != 0) + read_bindings(node->getChild("mod-shift"), binding_list, + modifiers|KEYMOD_SHIFT, module); + + if (node->getChild("mod-ctrl") != 0) + read_bindings(node->getChild("mod-ctrl"), binding_list, + modifiers|KEYMOD_CTRL, module); + + if (node->getChild("mod-alt") != 0) + read_bindings(node->getChild("mod-alt"), binding_list, + modifiers|KEYMOD_ALT, module); + + if (node->getChild("mod-meta") != 0) + read_bindings(node->getChild("mod-meta"), binding_list, + modifiers|KEYMOD_META, module); + + if (node->getChild("mod-super") != 0) + read_bindings(node->getChild("mod-super"), binding_list, + modifiers|KEYMOD_SUPER, module); + + if (node->getChild("mod-hyper") != 0) + read_bindings(node->getChild("mod-hyper"), binding_list, + modifiers|KEYMOD_HYPER, module); +} + diff --git a/src/Input/FGCommonInput.hxx b/src/Input/FGCommonInput.hxx new file mode 100644 index 000000000..7eee91526 --- /dev/null +++ b/src/Input/FGCommonInput.hxx @@ -0,0 +1,51 @@ +// FGCommonInput.hxx -- common functions for all Input subsystems +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef FGCOMMONINPUT_H +#define FGCOMMONINPUT_H + +#include +#include + +#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 > binding_list_t; + + /* + read all "binding" nodes directly under the specified base node and fill the + vector of SGBinding supplied in binding_list. Reads all the mod-xxx bindings and + add the corresponding SGBindings. + */ + static void read_bindings (const SGPropertyNode * base, binding_list_t * binding_list, int modifiers, string & module ); +}; + +#endif diff --git a/src/Input/FGDeviceConfigurationMap.cxx b/src/Input/FGDeviceConfigurationMap.cxx new file mode 100644 index 000000000..e54860dfe --- /dev/null +++ b/src/Input/FGDeviceConfigurationMap.cxx @@ -0,0 +1,77 @@ +// FGDeviceConfigurationMap.cxx -- a map to access xml device configuration +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGDeviceConfigurationMap.hxx" +#include +#include
+ +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 childNodes = base->getChildren(childname); + for (int k = (int)childNodes.size() - 1; k >= 0; k--) { + SGPropertyNode *n = childNodes[k]; + vector names = n->getChildren("name"); + if (names.size() ) // && (n->getChildren("axis").size() || n->getChildren("button").size())) + for (unsigned int j = 0; j < names.size(); j++) + (*this)[names[j]->getStringValue()] = n; + } +} + +FGDeviceConfigurationMap::~FGDeviceConfigurationMap() +{ + base->removeChildren( childname ); +} + +void FGDeviceConfigurationMap::scan_dir( SGPath & path, int *index) +{ + ulDir *dir = ulOpenDir(path.c_str()); + if (dir) { + ulDirEnt* dent; + while ((dent = ulReadDir(dir)) != 0) { + if (dent->d_name[0] == '.') + continue; + + SGPath p(path.str()); + p.append(dent->d_name); + scan_dir(p, index); + } + ulCloseDir(dir); + + } else if (path.extension() == "xml") { + SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path.str()); + SGPropertyNode_ptr n = base->getChild(childname, (*index)++, true); + readProperties(path.str(), n); + n->setStringValue("source", path.c_str()); + } +} + + diff --git a/src/Input/FGDeviceConfigurationMap.hxx b/src/Input/FGDeviceConfigurationMap.hxx new file mode 100644 index 000000000..27a3a8a14 --- /dev/null +++ b/src/Input/FGDeviceConfigurationMap.hxx @@ -0,0 +1,48 @@ +// FGDeviceConfigurationMap.hxx -- a map to access xml device configuration +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef _FGDEVICECONFIGURATIONMAP_HXX +#define _FGDEVICECONFIGURATIONMAP_HXX + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include +#include + +#include +using std::map; + +class FGDeviceConfigurationMap : public map { +public: + FGDeviceConfigurationMap ( const char * relative_path, SGPropertyNode_ptr base, const char * childname ); + virtual ~FGDeviceConfigurationMap(); +private: + void scan_dir( SGPath & path, int *index); + SGPropertyNode_ptr base; + const char * childname; +}; + +#endif diff --git a/src/Input/FGEventInput.cxx b/src/Input/FGEventInput.cxx new file mode 100644 index 000000000..bf0413671 --- /dev/null +++ b/src/Input/FGEventInput.cxx @@ -0,0 +1,203 @@ +// FGEventInput.cxx -- handle event driven input devices +// +// Written by Torsten Dreyer, started July 2009. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGEventInput.hxx" +#include
+#include +#include +#include + +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::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 eventNodes = deviceNode->getChildren( "event" ); + for( vector::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ ) { + FGInputEvent * p = FGInputEvent::NewObject( *it ); + if( p == NULL ) { + SG_LOG(SG_INPUT, SG_WARN, "Unhandled event/name in " << inputDevice->GetName() ); + continue; + } + inputDevice->AddHandledEvent( p ); + } + + // TODO: + // add nodes for the last event: + // last-event/name [string] + // last-event/value [double] + + try { + inputDevice->Open(); + input_devices[ deviceNode->getIndex() ] = inputDevice; + } + catch( ... ) { + SG_LOG(SG_INPUT, SG_WARN, "can't open InputDevice " << inputDevice->GetName() ); + } +} diff --git a/src/Input/FGEventInput.hxx b/src/Input/FGEventInput.hxx new file mode 100644 index 000000000..8b4e0d941 --- /dev/null +++ b/src/Input/FGEventInput.hxx @@ -0,0 +1,171 @@ +// FGEventInput.hxx -- handle event driven input devices +// +// Written by Torsten Dreyer, started July 2009 +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef __FGEVENTINPUT_HXX +#define __FGEVENTINPUT_HXX + +#include "FGCommonInput.hxx" +#include "FGButton.hxx" +#include "FGDeviceConfigurationMap.hxx" +#include + +/* + * 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. + * + * + * Change the view pitch + * rel-x-rotate + * + * property-adjust + * sim/current-view/pitch-offset-deg + * 0.01 + * -90.0 + * 90.0 + * false + * + * + */ +class FGInputEvent : public SGReferenced,FGCommonInput { +public: + /* + * Constructor for the class. The arg node shall point + * to the property corresponding to the 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_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 handledEvents; + string name; +}; + +typedef SGSharedPtr 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 input_devices; + FGDeviceConfigurationMap configMap; +}; + +#endif diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx new file mode 100644 index 000000000..75dc88700 --- /dev/null +++ b/src/Input/FGJoystickInput.cxx @@ -0,0 +1,306 @@ +// FGJoystickInput.cxx -- handle user input from joystick devices +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGJoystickInput.hxx" +#include "FGDeviceConfigurationMap.hxx" +#include
+#include + +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 << " 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 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 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 + // 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 buttons = js_node->getChildren("button"); + char buf[32]; + for (j = 0; j < buttons.size() && j < nbuttons; j++) { + const SGPropertyNode * button_node = buttons[j]; + const SGPropertyNode * num_node = button_node->getChild("number"); + size_t n_but = button_node->getIndex(); + if (num_node != 0) { + n_but = num_node->getIntValue(TGT_PLATFORM,n_but); + } + + if (n_but >= nbuttons) { + SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but); + continue; + } + + sprintf(buf, "%d", n_but); + SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << n_but); + bindings[i].buttons[n_but].init(button_node, buf, module ); + + // get interval-sec property + FGButton &b = bindings[i].buttons[n_but]; + if (button_node != 0) { + b.interval_sec = button_node->getDoubleValue("interval-sec",0.0); + b.last_dt = 0.0; + } + } + + js->setMinRange(minRange); + js->setMaxRange(maxRange); + js->setCenter(center); + } +} + +void FGJoystickInput::update( double dt ) +{ + float axis_values[MAX_JOYSTICK_AXES]; + int modifiers = fgGetKeyModifiers(); + int buttons; + + for (int i = 0; i < MAX_JOYSTICKS; i++) { + + jsJoystick * js = bindings[i].js; + if (js == 0 || js->notWorking()) + continue; + + js->read(&buttons, axis_values); + + // Fire bindings for the axes. + for (int j = 0; j < bindings[i].naxes; j++) { + axis &a = bindings[i].axes[j]; + + // Do nothing if the axis position + // is unchanged; only a change in + // position fires the bindings. + if (fabs(axis_values[j] - a.last_value) > a.tolerance) { + a.last_value = axis_values[j]; + for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++) + a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]); + } + + // do we have to emulate axis buttons? + a.last_dt += dt; + if(a.last_dt >= a.interval_sec) { + if (a.low.bindings[modifiers].size()) + bindings[i].axes[j].low.update( modifiers, axis_values[j] < a.low_threshold ); + + if (a.high.bindings[modifiers].size()) + bindings[i].axes[j].high.update( modifiers, axis_values[j] > a.high_threshold ); + + a.last_dt -= a.interval_sec; + } + } + + // Fire bindings for the buttons. + for (int j = 0; j < bindings[i].nbuttons; j++) { + FGButton &b = bindings[i].buttons[j]; + b.last_dt += dt; + if(b.last_dt >= b.interval_sec) { + bindings[i].buttons[j].update( modifiers, (buttons & (1 << j)) > 0 ); + b.last_dt -= b.interval_sec; + } + } + } +} + diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx new file mode 100644 index 000000000..ef1530ec0 --- /dev/null +++ b/src/Input/FGJoystickInput.hxx @@ -0,0 +1,88 @@ +// FGJoystickInput.hxx -- handle user input from joystick devices +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef _FGJOYSTICKINPUT_HXX +#define _FGJOYSTICKINPUT_HXX + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include "FGCommonInput.hxx" +#include "FGButton.hxx" +#include +#include + +//////////////////////////////////////////////////////////////////////// +// The Joystick Input Class +//////////////////////////////////////////////////////////////////////// +class FGJoystickInput : public SGSubsystem,FGCommonInput { +public: + FGJoystickInput(); + virtual ~FGJoystickInput(); + + virtual void init(); + virtual void postinit(); + virtual void update( double dt ); + + static const int MAX_JOYSTICKS = 10; + static const int MAX_JOYSTICK_AXES = _JS_MAX_AXES; + static const int MAX_JOYSTICK_BUTTONS = 32; + +private: + /** + * Settings for a single joystick axis. + */ + struct axis { + axis (); + virtual ~axis (); + float last_value; + float tolerance; + binding_list_t bindings[KEYMOD_MAX]; + float low_threshold; + float high_threshold; + FGButton low; + FGButton high; + float interval_sec; + double last_dt; + }; + + /** + * Settings for a joystick. + */ + struct joystick { + joystick (); + virtual ~joystick (); + int jsnum; + jsJoystick * js; + int naxes; + int nbuttons; + axis * axes; + FGButton * buttons; + }; + joystick bindings[MAX_JOYSTICKS]; + +}; + +#endif diff --git a/src/Input/FGKeyboardInput.cxx b/src/Input/FGKeyboardInput.cxx new file mode 100644 index 000000000..f4d20d66c --- /dev/null +++ b/src/Input/FGKeyboardInput.cxx @@ -0,0 +1,259 @@ +// FGKeyboardInput.cxx -- handle user input from keyboard devices +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGKeyboardInput.hxx" +#include
+#include +#include + +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 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 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(&_key_code)); + _key_event->tie("pressed", SGRawValuePointer(&_key_pressed)); + _key_event->tie("modifier", SGRawValuePointer(&_key_modifiers)); + _key_event->tie("modifier/shift", SGRawValuePointer(&_key_shift)); + _key_event->tie("modifier/ctrl", SGRawValuePointer(&_key_ctrl)); + _key_event->tie("modifier/alt", SGRawValuePointer(&_key_alt)); + _key_event->tie("modifier/meta", SGRawValuePointer(&_key_meta)); + _key_event->tie("modifier/super", SGRawValuePointer(&_key_super)); + _key_event->tie("modifier/hyper", SGRawValuePointer(&_key_hyper)); +} + +void FGKeyboardInput::unbind() +{ + fgUntie("/devices/status/keyboard"); + fgUntie("/devices/status/keyboard/shift"); + fgUntie("/devices/status/keyboard/ctrl"); + fgUntie("/devices/status/keyboard/alt"); + fgUntie("/devices/status/keyboard/meta"); + fgUntie("/devices/status/keyboard/super"); + fgUntie("/devices/status/keyboard/hyper"); + + _key_event->untie("key"); + _key_event->untie("pressed"); + _key_event->untie("modifier"); + _key_event->untie("modifier/shift"); + _key_event->untie("modifier/ctrl"); + _key_event->untie("modifier/alt"); + _key_event->untie("modifier/meta"); + _key_event->untie("modifier/super"); + _key_event->untie("modifier/hyper"); +} + +void FGKeyboardInput::update( double dt ) +{ + // nothing to do +} + +const FGCommonInput::binding_list_t & FGKeyboardInput::_find_key_bindings (unsigned int k, int modifiers) +{ + unsigned char kc = (unsigned char)k; + FGButton &b = bindings[k]; + + // Try it straight, first. + if (b.bindings[modifiers].size() > 0) + return b.bindings[modifiers]; + + // Alt-Gr is CTRL+ALT + else if (modifiers&(KEYMOD_CTRL|KEYMOD_ALT)) + return _find_key_bindings(k, modifiers&~(KEYMOD_CTRL|KEYMOD_ALT)); + + // Try removing the control modifier + // for control keys. + else if ((modifiers&KEYMOD_CTRL) && iscntrl(kc)) + return _find_key_bindings(k, modifiers&~KEYMOD_CTRL); + + // Try removing shift modifier + // for upper case or any punctuation + // (since different keyboards will + // shift different punctuation types) + else if ((modifiers&KEYMOD_SHIFT) && (isupper(kc) || ispunct(kc))) + return _find_key_bindings(k, modifiers&~KEYMOD_SHIFT); + + // Try removing alt modifier for + // high-bit characters. + else if ((modifiers&KEYMOD_ALT) && k >= 128 && k < 256) + return _find_key_bindings(k, modifiers&~KEYMOD_ALT); + + // Give up and return the empty vector. + else + return b.bindings[modifiers]; +} + +void FGKeyboardInput::doKey (int k, int modifiers, int x, int y) +{ + // Sanity check. + if (k < 0 || k >= MAX_KEYS) { + SG_LOG(SG_INPUT, SG_WARN, "Key value " << k << " out of range"); + return; + } + + _key_code = k; + _key_modifiers = modifiers >> 1; + _key_pressed = (modifiers & KEYMOD_RELEASED) == 0; + _key_shift = getModShift(); + _key_ctrl = getModCtrl(); + _key_alt = getModAlt(); + _key_meta = getModMeta(); + _key_super = getModSuper(); + _key_hyper = getModHyper(); + _key_event->fireValueChanged(); + if (_key_code < 0) + return; + + k = _key_code; + modifiers = _key_modifiers << 1; + if (!_key_pressed) + modifiers |= KEYMOD_RELEASED; + FGButton &b = bindings[k]; + + // Key pressed. + if (!(modifiers & KEYMOD_RELEASED)) { + SG_LOG( SG_INPUT, SG_DEBUG, "User pressed key " << k << " with modifiers " << modifiers ); + if (!b.last_state || b.is_repeatable) { + const binding_list_t &bindings = _find_key_bindings(k, modifiers); + + for (unsigned int i = 0; i < bindings.size(); i++) + bindings[i]->fire(); + b.last_state = 1; + } + } + // Key released. + else { + SG_LOG(SG_INPUT, SG_DEBUG, "User released key " << k << " with modifiers " << modifiers); + if (b.last_state) { + const binding_list_t &bindings = _find_key_bindings(k, modifiers); + for (unsigned int i = 0; i < bindings.size(); i++) + bindings[i]->fire(); + b.last_state = 0; + } + } +} + +void FGKeyboardInput::keyHandler(int key, int keymod, int mousex, int mousey) +{ + if((keymod & KEYMOD_RELEASED) == 0) + if(puKeyboard(key, PU_DOWN)) + return; + + if(keyboardInput) + keyboardInput->doKey(key, keymod, mousex, mousey); +} diff --git a/src/Input/FGKeyboardInput.hxx b/src/Input/FGKeyboardInput.hxx new file mode 100644 index 000000000..225d0f65e --- /dev/null +++ b/src/Input/FGKeyboardInput.hxx @@ -0,0 +1,71 @@ +// FGKeyboardInput.hxx -- handle user input from keyboard devices +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef _FGKEYBOARDINPUT_HXX +#define _FGKEYBOARDINPUT_HXX + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include "FGCommonInput.hxx" +#include "FGButton.hxx" +#include + +//////////////////////////////////////////////////////////////////////// +// The Keyboard Input Class +//////////////////////////////////////////////////////////////////////// +class FGKeyboardInput : public SGSubsystem,FGCommonInput { +public: + FGKeyboardInput(); + virtual ~FGKeyboardInput(); + + virtual void init(); + virtual void postinit(); + virtual void bind(); + virtual void unbind(); + virtual void update( double dt ); + + static const int MAX_KEYS = 1024; + +private: + const binding_list_t& _find_key_bindings (unsigned int k, int modifiers); + void doKey (int k, int modifiers, int x, int y); + + static void keyHandler(int key, int keymod, int mousex, int mousey); + static FGKeyboardInput * keyboardInput; + FGButton bindings[MAX_KEYS]; + SGPropertyNode_ptr _key_event; + int _key_code; + int _key_modifiers; + bool _key_pressed; + bool _key_shift; + bool _key_ctrl; + bool _key_alt; + bool _key_meta; + bool _key_super; + bool _key_hyper; +}; + +#endif diff --git a/src/Input/FGMouseInput.cxx b/src/Input/FGMouseInput.cxx new file mode 100644 index 000000000..c0b7e3f87 --- /dev/null +++ b/src/Input/FGMouseInput.cxx @@ -0,0 +1,363 @@ +// FGMouseInput.cxx -- handle user input from mouse devices +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include "FGMouseInput.hxx" + + +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 pickList; + if (FGRenderer::pick(pickList, ea)) { + std::vector::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 >::iterator li; + for (li = mi->second.begin(); li != mi->second.end(); ++li) { + (*li)->update(dt); + } + } +} + + +#include +#include +#include +//////////////////////////////////////////////////////////////////////// +// The Mouse Input Implementation +//////////////////////////////////////////////////////////////////////// + +const FGMouseInput::MouseCursorMap FGMouseInput::mouse_cursor_map[] = { + { "none", MOUSE_CURSOR_NONE }, + { "inherit", MOUSE_CURSOR_POINTER }, + { "wait", MOUSE_CURSOR_WAIT }, + { "crosshair", MOUSE_CURSOR_CROSSHAIR }, + { "left-right", MOUSE_CURSOR_LEFTRIGHT }, + { 0, 0 } +}; + +FGMouseInput * FGMouseInput::mouseInput = NULL; + +FGMouseInput::FGMouseInput() +{ + if( mouseInput == NULL ) + mouseInput = this; +} + +FGMouseInput::~FGMouseInput() +{ + if( mouseInput == this ) + mouseInput = NULL; +} + +void FGMouseInput::init() +{ + SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings"); + string module = ""; + + SGPropertyNode * mouse_nodes = fgGetNode("/input/mice"); + if (mouse_nodes == 0) { + SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!"); + mouse_nodes = fgGetNode("/input/mice", true); + } + + int j; + for (int i = 0; i < MAX_MICE; i++) { + SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true); + mouse &m = bindings[i]; + + // Grab node pointers + char buf[64]; + sprintf(buf, "/devices/status/mice/mouse[%d]/mode", i); + m.mode_node = fgGetNode(buf); + if (m.mode_node == NULL) { + m.mode_node = fgGetNode(buf, true); + m.mode_node->setIntValue(0); + } + for (j = 0; j < MAX_MOUSE_BUTTONS; j++) { + sprintf(buf, "/devices/status/mice/mouse[%d]/button[%d]", i, j); + m.mouse_button_nodes[j] = fgGetNode(buf, true); + m.mouse_button_nodes[j]->setBoolValue(false); + } + + // Read all the modes + m.nModes = mouse_node->getIntValue("mode-count", 1); + m.modes = new mouse_mode[m.nModes]; + + for (int j = 0; j < m.nModes; j++) { + int k; + + // Read the mouse cursor for this mode + SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true); + const char * cursor_name = + mode_node->getStringValue("cursor", "inherit"); + m.modes[j].cursor = MOUSE_CURSOR_POINTER; + for (k = 0; mouse_cursor_map[k].name != 0; k++) { + if (!strcmp(mouse_cursor_map[k].name, cursor_name)) { + m.modes[j].cursor = mouse_cursor_map[k].cursor; + break; + } + } + + // Read other properties for this mode + m.modes[j].constrained = mode_node->getBoolValue("constrained", false); + m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false); + + // Read the button bindings for this mode + m.modes[j].buttons = new FGButton[MAX_MOUSE_BUTTONS]; + char buf[32]; + for (k = 0; k < MAX_MOUSE_BUTTONS; k++) { + sprintf(buf, "mouse button %d", k); + SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k); + m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf, module ); + } + + // Read the axis bindings for this mode + read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module ); + read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module ); + } + } + + fgRegisterMouseClickHandler(mouseClickHandler); + fgRegisterMouseMotionHandler(mouseMotionHandler); +} + +void FGMouseInput::update ( double dt ) +{ + mouse &m = bindings[0]; + int mode = m.mode_node->getIntValue(); + if (mode != m.current_mode) { + m.current_mode = mode; + m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 ); + if (mode >= 0 && mode < m.nModes) { + fgSetMouseCursor(m.modes[mode].cursor); + m.x = fgGetInt("/sim/startup/xsize", 800) / 2; + m.y = fgGetInt("/sim/startup/ysize", 600) / 2; + fgWarpMouse(m.x, m.y); + } else { + SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range"); + fgSetMouseCursor(MOUSE_CURSOR_POINTER); + } + } + + if ( fgGetBool( "/sim/mouse/hide-cursor", true ) ) { + if ( m.x != m.save_x || m.y != m.save_y ) { + m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 ); + if (fgGetMouseCursor() == MOUSE_CURSOR_NONE) + fgSetMouseCursor(m.modes[mode].cursor); + } else { + m.timeout -= dt; + if ( m.timeout <= 0.0 ) { + fgSetMouseCursor(MOUSE_CURSOR_NONE); + m.timeout = 0.0; + } + } + m.save_x = m.x; + m.save_y = m.y; + } + + activePickCallbacks.update( dt ); +} + +FGMouseInput::mouse::mouse () + : x(-1), + y(-1), + save_x(-1), + save_y(-1), + nModes(1), + current_mode(0), + modes(NULL) +{ +} + +FGMouseInput::mouse::~mouse () +{ + delete [] modes; +} + +FGMouseInput::mouse_mode::mouse_mode () + : cursor(MOUSE_CURSOR_POINTER), + constrained(false), + pass_through(false), + buttons(NULL) +{ +} + +FGMouseInput::mouse_mode::~mouse_mode () +{ + // FIXME: memory leak +// for (int i = 0; i < KEYMOD_MAX; i++) { +// int j; +// for (j = 0; i < x_bindings[i].size(); j++) +// delete bindings[i][j]; +// for (j = 0; j < y_bindings[i].size(); j++) +// delete bindings[i][j]; +// } + delete [] buttons; +} + +void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea) +{ + int modifiers = fgGetKeyModifiers(); + + mouse &m = bindings[0]; + mouse_mode &mode = m.modes[m.current_mode]; + + // Let the property manager know. + if (b >= 0 && b < MAX_MOUSE_BUTTONS) + m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN); + + // Pass on to PUI and the panel if + // requested, and return if one of + // them consumes the event. + + if (updown != MOUSE_BUTTON_DOWN) { + // Execute the mouse up event in any case, may be we should + // stop processing here? + while (!activePickCallbacks[b].empty()) { + activePickCallbacks[b].front()->buttonReleased(); + activePickCallbacks[b].pop_front(); + } + } + + if (mode.pass_through) { + if (0 <= x && 0 <= y && puMouse(b, updown, x, y)) + return; + else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) && + globals->get_current_panel()->getVisibility() && + globals->get_current_panel()->doMouseAction(b, updown, x, y)) + return; + else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y)) + return; + else { + // pui didn't want the click event so compute a + // scenegraph intersection point corresponding to the mouse click + if (updown == MOUSE_BUTTON_DOWN) { + activePickCallbacks.init( b, ea ); + } + } + } + + // OK, PUI and the panel didn't want the click + if (b >= MAX_MOUSE_BUTTONS) { + SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b + << " where only " << MAX_MOUSE_BUTTONS << " expected"); + return; + } + + m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y); +} + +void FGMouseInput::doMouseMotion (int x, int y) +{ + // Don't call fgGetKeyModifiers() here, until we are using a + // toolkit that supports getting the mods from outside a key + // callback. Glut doesn't. + int modifiers = KEYMOD_NONE; + + int xsize = fgGetInt("/sim/startup/xsize", 800); + int ysize = fgGetInt("/sim/startup/ysize", 600); + + mouse &m = bindings[0]; + + if (m.current_mode < 0 || m.current_mode >= m.nModes) { + m.x = x; + m.y = y; + return; + } + mouse_mode &mode = m.modes[m.current_mode]; + + // Pass on to PUI if requested, and return + // if PUI consumed the event. + if (mode.pass_through && puMouse(x, y)) { + m.x = x; + m.y = y; + return; + } + + // OK, PUI didn't want the event, + // so we can play with it. + if (x != m.x) { + int delta = x - m.x; + for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++) + mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize)); + } + if (y != m.y) { + int delta = y - m.y; + for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++) + mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize)); + } + + // Constrain the mouse if requested + if (mode.constrained) { + bool need_warp = false; + if (x <= (xsize * .25) || x >= (xsize * .75)) { + x = int(xsize * .5); + need_warp = true; + } + + if (y <= (ysize * .25) || y >= (ysize * .75)) { + y = int(ysize * .5); + need_warp = true; + } + + if (need_warp) + fgWarpMouse(x, y); + } + + if (m.x != x) + fgSetInt("/devices/status/mice/mouse/x", m.x = x); + + if (m.y != y) + fgSetInt("/devices/status/mice/mouse/y", m.y = y); +} + +void FGMouseInput::mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea) +{ + if(mouseInput) + mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea); +} + +void FGMouseInput::mouseMotionHandler(int x, int y) +{ + if (mouseInput != 0) + mouseInput->doMouseMotion(x, y); +} + + diff --git a/src/Input/FGMouseInput.hxx b/src/Input/FGMouseInput.hxx new file mode 100644 index 000000000..7e1d5abb2 --- /dev/null +++ b/src/Input/FGMouseInput.hxx @@ -0,0 +1,123 @@ +// FGMouseInput.hxx -- handle user input from mouse devices +// +// Written by Torsten Dreyer, started August 2009 +// Based on work from David Megginson, started May 2001. +// +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de +// Copyright (C) 2001 David Megginson, david@megginson.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifndef _FGMOUSEINPUT_HXX +#define _FGMOUSEINPUT_HXX + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include "FGCommonInput.hxx" +#include "FGButton.hxx" + +#include +#include +#include +#include +#include
+/** + * List of currently pressed mouse button events + */ +class ActivePickCallbacks : public std::map > > { +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 diff --git a/src/Input/Makefile.am b/src/Input/Makefile.am index df99e01f1..d51d2887c 100644 --- a/src/Input/Makefile.am +++ b/src/Input/Makefile.am @@ -2,7 +2,12 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkgdatadir)\" noinst_LIBRARIES = libInput.a -libInput_a_SOURCES = input.cxx input.hxx +libInput_a_SOURCES = input.cxx input.hxx FGCommonInput.cxx FGCommonInput.hxx \ + FGDeviceConfigurationMap.cxx FGDeviceConfigurationMap.hxx \ + FGButton.cxx FGButton.hxx \ + FGMouseInput.cxx FGMouseInput.hxx \ + FGKeyboardInput.cxx FGKeyboardInput.hxx \ + FGJoystickInput.cxx FGJoystickInput.hxx bin_PROGRAMS = js_demo fgjs diff --git a/src/Input/input.cxx b/src/Input/input.cxx index 6fc8bc997..6a36f9d80 100644 --- a/src/Input/input.cxx +++ b/src/Input/input.cxx @@ -1,8 +1,10 @@ // input.cxx -- handle user input from various sources. // // Written by David Megginson, started May 2001. +// Major redesign by Torsten Dreyer, started August 2009 // // Copyright (C) 2001 David Megginson, david@megginson.com +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -24,1154 +26,29 @@ # include #endif -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include
-#include
- #include "input.hxx" +#include "FGMouseInput.hxx" +#include "FGKeyboardInput.hxx" +#include "FGJoystickInput.hxx" -#include -#include
+#ifdef WITH_EVENTINPUT +#include "FGLinuxEventInput.hxx" +#endif -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); - - -//////////////////////////////////////////////////////////////////////// -// Local variables. -//////////////////////////////////////////////////////////////////////// - -static FGInput * default_input = 0; - - -//////////////////////////////////////////////////////////////////////// -// 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; -} - - //////////////////////////////////////////////////////////////////////// // 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; + // SGSubsystemGroup deletes all subsystem in it's destructor } -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(&_key_code)); - _key_event->tie("pressed", SGRawValuePointer(&_key_pressed)); - _key_event->tie("modifier", SGRawValuePointer(&_key_modifiers)); - _key_event->tie("modifier/shift", SGRawValuePointer(&_key_shift)); - _key_event->tie("modifier/ctrl", SGRawValuePointer(&_key_ctrl)); - _key_event->tie("modifier/alt", SGRawValuePointer(&_key_alt)); - _key_event->tie("modifier/meta", SGRawValuePointer(&_key_meta)); - _key_event->tie("modifier/super", SGRawValuePointer(&_key_super)); - _key_event->tie("modifier/hyper", SGRawValuePointer(&_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 pickList; - if (FGRenderer::pick(pickList, ea)) { - std::vector::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 (reverse order) - map jsmap; - vector js_named = js_nodes->getChildren("js-named"); - - for (int k = (int)js_named.size() - 1; k >= 0; k--) { - SGPropertyNode *n = js_named[k]; - vector 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 + " 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 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 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 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 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 - // 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 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 > >::iterator mi; - for (mi = _activePickCallbacks.begin(); - mi != _activePickCallbacks.end(); ++mi) { - std::list >::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 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]; -} - - - -//////////////////////////////////////////////////////////////////////// -// 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]; -} - - - -//////////////////////////////////////////////////////////////////////// -// 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]; -} - - - -//////////////////////////////////////////////////////////////////////// -// Implementation of FGInput::joystick. -//////////////////////////////////////////////////////////////////////// - -FGInput::joystick::joystick () - : jsnum(0), - js(0), - naxes(0), - nbuttons(0), - axes(0), - buttons(0) -{ -} - -FGInput::joystick::~joystick () -{ -// delete js; - delete[] axes; - delete[] buttons; -} - - - -//////////////////////////////////////////////////////////////////////// -// 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; -} - - - -//////////////////////////////////////////////////////////////////////// -// 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); -} diff --git a/src/Input/input.hxx b/src/Input/input.hxx index e71d8620e..f26f21a93 100644 --- a/src/Input/input.hxx +++ b/src/Input/input.hxx @@ -1,8 +1,10 @@ // input.hxx -- handle user input from various sources. // // Written by David Megginson, started May 2001. +// Major redesign by Torsten Dreyer, started August 2009 // // Copyright (C) 2001 David Megginson, david@megginson.com +// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -28,40 +30,10 @@ # error This library requires C++ #endif -#include -#include - -#include - -#include #include -#include -#include -#include -#include - -#include
-#include
-#include
- -#include -#include -#include - -using std::map; -using std::vector; - -#if defined( UL_WIN32 ) -#define TGT_PLATFORM "windows" -#elif defined ( UL_MAC_OSX ) -#define TGT_PLATFORM "mac" -#else -#define TGT_PLATFORM "unix" -#endif - //////////////////////////////////////////////////////////////////////// @@ -76,7 +48,7 @@ using std::vector; * keyboard, joystick, mouse, or even panel switches -- in a consistent * way, and to allow users to rebind any of the actions at runtime.

*/ -class FGInput : public SGSubsystem +class FGInput : public SGSubsystemGroup { public: /** @@ -89,259 +61,6 @@ public: */ virtual ~FGInput(); - // - // Implementation of SGSubsystem. - // - virtual void init (); - virtual void reinit (); - virtual void postinit (); - virtual void bind (); - virtual void unbind (); - virtual void update (double dt); - virtual void suspend (); - virtual void resume (); - virtual bool is_suspended () const; - - - /** - * Control whether this is the default module to receive events. - * - * The first input module created will set itself as the default - * automatically. - * - * @param status true if this should be the default module for - * events, false otherwise. - */ - virtual void makeDefault (bool status = true); - - - /** - * Handle a single keystroke. - * - * @param k The integer key code, see Main/fg_os.hxx - * @param modifiers Modifier keys pressed (bitfield). - * @param x The mouse x position at the time of keypress. - * @param y The mouse y position at the time of keypress. - */ - virtual void doKey (int k, int modifiers, int x, int y); - - - /** - * Handle a mouse click event. - * - * @param button The mouse button selected. - * @param updown Button status. - * @param x The X position of the mouse event, in screen coordinates. - * @param y The Y position of the mouse event, in screen coordinates. - */ - virtual void doMouseClick (int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*); - - - /** - * Handle mouse motion. - * - * @param x The new mouse x position, in screen coordinates. - * @param y The new mouse y position, in screen coordinates. - */ - virtual void doMouseMotion (int x, int y); - - -private: - // Constants - enum - { - MAX_KEYS = 1024, - MAX_JOYSTICKS = 10, - MAX_JOYSTICK_AXES = _JS_MAX_AXES, - MAX_JOYSTICK_BUTTONS = 32, - - MAX_MICE = 1, - MAX_MOUSE_BUTTONS = 8 - }; - struct mouse; - friend struct mouse; - - typedef vector > 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 > > _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