1
0
Fork 0

Advanced input subsystem - Step2: Split up current input subsystem

- encapsulate code into classes
- create separate subsystem for keyboard, mouse and joystick
- group new subsystems into subsystemgroup "input"
This commit is contained in:
torsten 2009-08-07 18:56:48 +00:00 committed by Tim Moore
parent aac4bec5c7
commit aea9c750f3
17 changed files with 1972 additions and 1421 deletions

75
src/Input/FGButton.cxx Normal file
View file

@ -0,0 +1,75 @@
// FGButton.cxx -- a simple button/key wrapper class
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGButton.hxx"
FGButton::FGButton ()
: is_repeatable(false),
interval_sec(0),
last_dt(0),
last_state(0)
{
}
FGButton::~FGButton ()
{
// bindings is a list of SGSharedPtr<SGBindings>
// no cleanup required
}
void FGButton::init( const SGPropertyNode * node, const string name, string & module )
{
if (node == 0) {
SG_LOG(SG_INPUT, SG_DEBUG, "No bindings for button " << name);
} else {
is_repeatable = node->getBoolValue("repeatable", is_repeatable);
// Get the bindings for the button
read_bindings( node, bindings, KEYMOD_NONE, module );
}
}
void FGButton::update( int modifiers, bool pressed, int x, int y)
{
if (pressed) {
// The press event may be repeated.
if (!last_state || is_repeatable) {
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
for (unsigned int k = 0; k < bindings[modifiers].size(); k++) {
bindings[modifiers][k]->fire(x, y);
}
}
} else {
// The release event is never repeated.
if (last_state) {
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" );
for (unsigned int k = 0; k < bindings[modifiers|KEYMOD_RELEASED].size(); k++)
bindings[modifiers|KEYMOD_RELEASED][k]->fire(x, y);
}
}
last_state = pressed;
}

44
src/Input/FGButton.hxx Normal file
View file

@ -0,0 +1,44 @@
// FGButton.hxx -- a simple button/key wrapper class
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef FGBUTTON_H
#define FGBUTTON_H
#include "FGCommonInput.hxx"
#include <Main/fg_os.hxx>
class FGButton : public FGCommonInput {
public:
FGButton();
virtual ~FGButton();
void init( const SGPropertyNode * node, const string name, string & module );
void update( int modifiers, bool pressed, int x = -1, int y = -1);
bool is_repeatable;
float interval_sec;
float last_dt;
int last_state;
binding_list_t bindings[KEYMOD_MAX];
};
#endif

View file

@ -0,0 +1,71 @@
// FGCommonInput.cxx -- common functions for all Input subsystems
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGCommonInput.hxx"
#include <Main/globals.hxx>
#include <Main/fg_os.hxx>
void FGCommonInput::read_bindings (const SGPropertyNode * node, binding_list_t * binding_list, int modifiers, string & module )
{
SG_LOG(SG_INPUT, SG_DEBUG, "Reading all bindings");
vector<SGPropertyNode_ptr> bindings = node->getChildren("binding");
string nasal = "nasal";
for (unsigned int i = 0; i < bindings.size(); i++) {
const char *cmd = bindings[i]->getStringValue("command");
SG_LOG(SG_INPUT, SG_DEBUG, "Reading binding " << cmd);
if (nasal.compare(cmd) == 0 && !module.empty())
bindings[i]->setStringValue("module", module.c_str());
binding_list[modifiers].push_back(new SGBinding(bindings[i], globals->get_props()));
}
// Read nested bindings for modifiers
if (node->getChild("mod-up") != 0)
read_bindings(node->getChild("mod-up"), binding_list,
modifiers|KEYMOD_RELEASED, module);
if (node->getChild("mod-shift") != 0)
read_bindings(node->getChild("mod-shift"), binding_list,
modifiers|KEYMOD_SHIFT, module);
if (node->getChild("mod-ctrl") != 0)
read_bindings(node->getChild("mod-ctrl"), binding_list,
modifiers|KEYMOD_CTRL, module);
if (node->getChild("mod-alt") != 0)
read_bindings(node->getChild("mod-alt"), binding_list,
modifiers|KEYMOD_ALT, module);
if (node->getChild("mod-meta") != 0)
read_bindings(node->getChild("mod-meta"), binding_list,
modifiers|KEYMOD_META, module);
if (node->getChild("mod-super") != 0)
read_bindings(node->getChild("mod-super"), binding_list,
modifiers|KEYMOD_SUPER, module);
if (node->getChild("mod-hyper") != 0)
read_bindings(node->getChild("mod-hyper"), binding_list,
modifiers|KEYMOD_HYPER, module);
}

View file

@ -0,0 +1,51 @@
// FGCommonInput.hxx -- common functions for all Input subsystems
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef FGCOMMONINPUT_H
#define FGCOMMONINPUT_H
#include <vector>
#include <simgear/structure/SGBinding.hxx>
#if defined( UL_WIN32 )
#define TGT_PLATFORM "windows"
#elif defined ( UL_MAC_OSX )
#define TGT_PLATFORM "mac"
#else
#define TGT_PLATFORM "unix"
#endif
class FGCommonInput {
public:
typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
/*
read all "binding" nodes directly under the specified base node and fill the
vector of SGBinding supplied in binding_list. Reads all the mod-xxx bindings and
add the corresponding SGBindings.
*/
static void read_bindings (const SGPropertyNode * base, binding_list_t * binding_list, int modifiers, string & module );
};
#endif

View file

@ -0,0 +1,77 @@
// FGDeviceConfigurationMap.cxx -- a map to access xml device configuration
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGDeviceConfigurationMap.hxx"
#include <simgear/props/props_io.hxx>
#include <Main/globals.hxx>
FGDeviceConfigurationMap::FGDeviceConfigurationMap( const char * relative_path, SGPropertyNode_ptr aBase, const char * aChildname ) :
base(aBase),
childname(aChildname)
{
SGPath path(globals->get_fg_root());
path.append( relative_path );
int index = 1000;
scan_dir( path, &index);
vector<SGPropertyNode_ptr> childNodes = base->getChildren(childname);
for (int k = (int)childNodes.size() - 1; k >= 0; k--) {
SGPropertyNode *n = childNodes[k];
vector<SGPropertyNode_ptr> names = n->getChildren("name");
if (names.size() ) // && (n->getChildren("axis").size() || n->getChildren("button").size()))
for (unsigned int j = 0; j < names.size(); j++)
(*this)[names[j]->getStringValue()] = n;
}
}
FGDeviceConfigurationMap::~FGDeviceConfigurationMap()
{
base->removeChildren( childname );
}
void FGDeviceConfigurationMap::scan_dir( SGPath & path, int *index)
{
ulDir *dir = ulOpenDir(path.c_str());
if (dir) {
ulDirEnt* dent;
while ((dent = ulReadDir(dir)) != 0) {
if (dent->d_name[0] == '.')
continue;
SGPath p(path.str());
p.append(dent->d_name);
scan_dir(p, index);
}
ulCloseDir(dir);
} else if (path.extension() == "xml") {
SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path.str());
SGPropertyNode_ptr n = base->getChild(childname, (*index)++, true);
readProperties(path.str(), n);
n->setStringValue("source", path.c_str());
}
}

View file

@ -0,0 +1,48 @@
// FGDeviceConfigurationMap.hxx -- a map to access xml device configuration
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FGDEVICECONFIGURATIONMAP_HXX
#define _FGDEVICECONFIGURATIONMAP_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/props/props.hxx>
#include <simgear/misc/sg_path.hxx>
#include <map>
using std::map;
class FGDeviceConfigurationMap : public map<string,SGPropertyNode_ptr> {
public:
FGDeviceConfigurationMap ( const char * relative_path, SGPropertyNode_ptr base, const char * childname );
virtual ~FGDeviceConfigurationMap();
private:
void scan_dir( SGPath & path, int *index);
SGPropertyNode_ptr base;
const char * childname;
};
#endif

203
src/Input/FGEventInput.cxx Normal file
View file

@ -0,0 +1,203 @@
// FGEventInput.cxx -- handle event driven input devices
//
// Written by Torsten Dreyer, started July 2009.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGEventInput.hxx"
#include <Main/fg_props.hxx>
#include <simgear/io/sg_file.hxx>
#include <poll.h>
#include <linux/input.h>
static inline bool StartsWith( string & s, const char * cp )
{
return s.compare( 0, strlen(cp), cp ) == 0;
}
FGInputEvent * FGInputEvent::NewObject( SGPropertyNode_ptr node )
{
string name = node->getStringValue( "name" );
if( StartsWith( name, "button-" ) )
return new FGButtonEvent( node );
if( StartsWith( name, "rel-" ) )
return new FGAxisEvent( node );
if( StartsWith( name, "abs-" ) )
return new FGAxisEvent( node );
return NULL;
}
FGInputEvent::FGInputEvent( SGPropertyNode_ptr node ) :
lastDt(0.0)
{
name = node->getStringValue( "name" );
desc = node->getStringValue( "desc" );
intervalSec = node->getDoubleValue("interval-sec",0.0);
string module = "event";
read_bindings( node, bindings, KEYMOD_NONE, module );
}
FGInputEvent::~FGInputEvent()
{
}
void FGInputEvent::fire( FGEventData & eventData )
{
lastDt += eventData.dt;
if( lastDt >= intervalSec ) {
for( binding_list_t::iterator it = bindings[KEYMOD_NONE].begin(); it != bindings[KEYMOD_NONE].end(); it++ )
(*it)->fire( eventData.value, 1.0 );
lastDt -= intervalSec;
}
}
FGAxisEvent::FGAxisEvent( SGPropertyNode_ptr node ) :
FGInputEvent( node )
{
tolerance = node->getDoubleValue("tolerance", 0.002);
minRange = node->getDoubleValue("min-range", -1024.0);
maxRange = node->getDoubleValue("max-range", 1024.0);
center = node->getDoubleValue("center", 0.0);
deadband = node->getDoubleValue("dead-band", 0.0);
lowThreshold = node->getDoubleValue("low-threshold", -0.9);
highThreshold = node->getDoubleValue("high-threshold", 0.9);
lastValue = 9999999;
}
void FGAxisEvent::fire( FGEventData & eventData )
{
if (fabs( eventData.value - lastValue) < tolerance)
return;
lastValue = eventData.value;
FGInputEvent::fire( eventData );
}
FGButtonEvent::FGButtonEvent( SGPropertyNode_ptr node ) :
FGInputEvent( node )
{
}
void FGButtonEvent::fire( FGEventData & eventData )
{
FGInputEvent::fire( eventData );
}
FGInputDevice::~FGInputDevice()
{
}
void FGInputDevice::HandleEvent( FGEventData & eventData )
{
string eventName = TranslateEventName( eventData );
cout << GetName() << " has event " << eventName << endl;
if( handledEvents.count( eventName ) > 0 ) {
handledEvents[ eventName ]->fire( eventData );
}
}
void FGInputDevice::SetName( string name )
{
this->name = name;
}
const char * FGEventInput::PROPERTY_ROOT = "/input/event";
FGEventInput::FGEventInput() :
configMap( "Input/Event", fgGetNode( PROPERTY_ROOT, true ), "device-named" )
{
}
FGEventInput::~FGEventInput()
{
for( map<int,FGInputDevice*>::iterator it = input_devices.begin(); it != input_devices.end(); it++ )
delete (*it).second;
input_devices.clear();
}
void FGEventInput::init( )
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing event bindings");
SGPropertyNode * base = fgGetNode("/input/event", true);
}
void FGEventInput::postinit ()
{
}
void FGEventInput::AddDevice( FGInputDevice * inputDevice )
{
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
SGPropertyNode_ptr deviceNode = NULL;
// look for configuration in the device map
if( configMap.count( inputDevice->GetName() ) > 0 ) {
// found - copy to /input/event/device[n]
// find a free index
unsigned index;
for( index = 0; index < 1000; index++ )
if( (deviceNode = baseNode->getNode( "device", index, false ) ) == NULL )
break;
if( index == 1000 ) {
SG_LOG(SG_INPUT, SG_WARN, "To many event devices - ignoring " << inputDevice->GetName() );
return;
}
// create this node
deviceNode = baseNode->getNode( "device", index, true );
// and copy the properties from the configuration tree
copyProperties( configMap[ inputDevice->GetName() ], deviceNode );
}
if( deviceNode == NULL ) {
SG_LOG(SG_INPUT, SG_WARN, "No configuration found for device " << inputDevice->GetName() );
return;
}
vector<SGPropertyNode_ptr> eventNodes = deviceNode->getChildren( "event" );
for( vector<SGPropertyNode_ptr>::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ ) {
FGInputEvent * p = FGInputEvent::NewObject( *it );
if( p == NULL ) {
SG_LOG(SG_INPUT, SG_WARN, "Unhandled event/name in " << inputDevice->GetName() );
continue;
}
inputDevice->AddHandledEvent( p );
}
// TODO:
// add nodes for the last event:
// last-event/name [string]
// last-event/value [double]
try {
inputDevice->Open();
input_devices[ deviceNode->getIndex() ] = inputDevice;
}
catch( ... ) {
SG_LOG(SG_INPUT, SG_WARN, "can't open InputDevice " << inputDevice->GetName() );
}
}

171
src/Input/FGEventInput.hxx Normal file
View file

@ -0,0 +1,171 @@
// FGEventInput.hxx -- handle event driven input devices
//
// Written by Torsten Dreyer, started July 2009
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef __FGEVENTINPUT_HXX
#define __FGEVENTINPUT_HXX
#include "FGCommonInput.hxx"
#include "FGButton.hxx"
#include "FGDeviceConfigurationMap.hxx"
#include <simgear/structure/subsystem_mgr.hxx>
/*
* A base class for event data.
*/
struct FGEventData {
FGEventData( double aValue, double aDt ) : value(aValue), dt(aDt) {}
double value;
double dt;
};
/*
* A wrapper class for a configured event.
*
* <event>
* <desc>Change the view pitch</desc>
* <name>rel-x-rotate</name>
* <binding>
* <command>property-adjust</command>
* <property>sim/current-view/pitch-offset-deg</property>
* <factor type="double">0.01</factor>
* <min type="double">-90.0</min>
* <max type="double">90.0</max>
* <wrap type="bool">false</wrap>
* </binding>
* </event>
*/
class FGInputEvent : public SGReferenced,FGCommonInput {
public:
/*
* Constructor for the class. The arg node shall point
* to the property corresponding to the <event> node
*/
FGInputEvent( SGPropertyNode_ptr node );
virtual ~FGInputEvent();
/*
* dispatch the event value through all bindings
*/
virtual void fire( FGEventData & eventData );
/*
* access for the name property
*/
string GetName() const { return name; }
/*
* access for the description property
*/
string GetDescription() const { return desc; }
static FGInputEvent * NewObject( SGPropertyNode_ptr node );
protected:
/* A more or less meaningfull description of the event */
string desc;
/* One of the predefined names of the event */
string name;
/* A list of SGBinding objects */
binding_list_t bindings[KEYMOD_MAX];
double lastDt;
double intervalSec;
};
class FGButtonEvent : public FGInputEvent {
public:
FGButtonEvent( SGPropertyNode_ptr node );
virtual void fire( FGEventData & eventData );
};
class FGAxisEvent : public FGInputEvent {
public:
FGAxisEvent( SGPropertyNode_ptr node );
protected:
virtual void fire( FGEventData & eventData );
double tolerance;
double minRange;
double maxRange;
double center;
double deadband;
double lowThreshold;
double highThreshold;
double lastValue;
};
typedef class SGSharedPtr<FGInputEvent> FGInputEvent_ptr;
/*
* A abstract class implementing basic functionality of input devices for
* all operating systems. This is the base class for the O/S-specific
* implementation of input device handlers
*/
class FGInputDevice : public SGReferenced {
public:
FGInputDevice() {}
FGInputDevice( string aName ) : name(aName) {}
virtual ~FGInputDevice();
virtual void Open() = 0;
virtual void Close() = 0;
virtual const char * TranslateEventName( FGEventData & eventData ) = 0;
void SetName( string name );
string & GetName() { return name; }
void HandleEvent( FGEventData & eventData );
void AddHandledEvent( FGInputEvent_ptr handledEvent ) {
if( handledEvents.count( handledEvent->GetName() ) == 0 )
handledEvents[handledEvent->GetName()] = handledEvent;
}
private:
map<string,FGInputEvent_ptr> handledEvents;
string name;
};
typedef SGSharedPtr<FGInputDevice> FGInputDevice_ptr;
/*
* The Subsystem for the event input device
*/
class FGEventInput : public SGSubsystem,FGCommonInput {
public:
FGEventInput();
virtual ~FGEventInput();
virtual void init();
virtual void postinit();
protected:
static const char * PROPERTY_ROOT;
void AddDevice( FGInputDevice * inputDevice );
map<int,FGInputDevice*> input_devices;
FGDeviceConfigurationMap configMap;
};
#endif

View file

@ -0,0 +1,306 @@
// FGJoystickInput.cxx -- handle user input from joystick devices
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGJoystickInput.hxx"
#include "FGDeviceConfigurationMap.hxx"
#include <Main/fg_props.hxx>
#include <Scripting/NasalSys.hxx>
FGJoystickInput::axis::axis ()
: last_value(9999999),
tolerance(0.002),
low_threshold(-0.9),
high_threshold(0.9),
interval_sec(0),
last_dt(0)
{
}
FGJoystickInput::axis::~axis ()
{
}
FGJoystickInput::joystick::joystick ()
: jsnum(0),
js(0),
naxes(0),
nbuttons(0),
axes(0),
buttons(0)
{
}
FGJoystickInput::joystick::~joystick ()
{
// delete js? why not?
// delete js;
delete[] axes;
delete[] buttons;
}
FGJoystickInput::FGJoystickInput()
{
}
FGJoystickInput::~FGJoystickInput()
{
}
void FGJoystickInput::init()
{
jsInit();
// TODO: zero the old bindings first.
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
FGDeviceConfigurationMap configMap("Input/Joysticks", js_nodes, "js-named");
for (int i = 0; i < MAX_JOYSTICKS; i++) {
jsJoystick * js = new jsJoystick(i);
bindings[i].js = js;
if (js->notWorking()) {
SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
continue;
}
const char * name = js->getName();
SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
if (js_node) {
SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
} else {
SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
SGPropertyNode_ptr named;
if ((named = configMap[name])) {
string source = named->getStringValue("source", "user defined");
SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
} else if ((named = configMap["default"])) {
string source = named->getStringValue("source", "user defined");
SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
<< "\"\nUsing default: \"" << source << '"');
} else {
SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
}
js_node = js_nodes->getChild("js", i, true);
copyProperties(named, js_node);
js_node->setStringValue("id", name);
}
}
}
void FGJoystickInput::postinit()
{
FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
for (int i = 0; i < MAX_JOYSTICKS; i++) {
SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
jsJoystick *js = bindings[i].js;
if (!js_node || js->notWorking())
continue;
#ifdef WIN32
JOYCAPS jsCaps ;
joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
unsigned int nbuttons = jsCaps.wNumButtons;
if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
#else
unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
#endif
int naxes = js->getNumAxes();
if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
bindings[i].naxes = naxes;
bindings[i].nbuttons = nbuttons;
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
// Set up range arrays
float minRange[MAX_JOYSTICK_AXES];
float maxRange[MAX_JOYSTICK_AXES];
float center[MAX_JOYSTICK_AXES];
// Initialize with default values
js->getMinRange(minRange);
js->getMaxRange(maxRange);
js->getCenter(center);
// Allocate axes and buttons
bindings[i].axes = new axis[naxes];
bindings[i].buttons = new FGButton[nbuttons];
//
// Initialize nasal groups.
//
ostringstream str;
str << "__js" << i;
string module = str.str();
nasalsys->createModule(module.c_str(), module.c_str(), "", 0);
vector<SGPropertyNode_ptr> nasal = js_node->getChildren("nasal");
unsigned int j;
for (j = 0; j < nasal.size(); j++) {
nasal[j]->setStringValue("module", module.c_str());
nasalsys->handleCommand(nasal[j]);
}
//
// Initialize the axes.
//
vector<SGPropertyNode_ptr> axes = js_node->getChildren("axis");
size_t nb_axes = axes.size();
for (j = 0; j < nb_axes; j++ ) {
const SGPropertyNode * axis_node = axes[j];
const SGPropertyNode * num_node = axis_node->getChild("number");
int n_axis = axis_node->getIndex();
if (num_node != 0) {
n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
// Silently ignore platforms that are not specified within the
// <number></number> section
if (n_axis < 0)
continue;
}
if (n_axis >= naxes) {
SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
continue;
}
axis &a = bindings[i].axes[n_axis];
js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
read_bindings(axis_node, a.bindings, KEYMOD_NONE, module );
// Initialize the virtual axis buttons.
a.low.init(axis_node->getChild("low"), "low", module );
a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
a.high.init(axis_node->getChild("high"), "high", module );
a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
a.last_dt = 0.0;
}
//
// Initialize the buttons.
//
vector<SGPropertyNode_ptr> buttons = js_node->getChildren("button");
char buf[32];
for (j = 0; j < buttons.size() && j < nbuttons; j++) {
const SGPropertyNode * button_node = buttons[j];
const SGPropertyNode * num_node = button_node->getChild("number");
size_t n_but = button_node->getIndex();
if (num_node != 0) {
n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
}
if (n_but >= nbuttons) {
SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
continue;
}
sprintf(buf, "%d", n_but);
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << n_but);
bindings[i].buttons[n_but].init(button_node, buf, module );
// get interval-sec property
FGButton &b = bindings[i].buttons[n_but];
if (button_node != 0) {
b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
b.last_dt = 0.0;
}
}
js->setMinRange(minRange);
js->setMaxRange(maxRange);
js->setCenter(center);
}
}
void FGJoystickInput::update( double dt )
{
float axis_values[MAX_JOYSTICK_AXES];
int modifiers = fgGetKeyModifiers();
int buttons;
for (int i = 0; i < MAX_JOYSTICKS; i++) {
jsJoystick * js = bindings[i].js;
if (js == 0 || js->notWorking())
continue;
js->read(&buttons, axis_values);
// Fire bindings for the axes.
for (int j = 0; j < bindings[i].naxes; j++) {
axis &a = bindings[i].axes[j];
// Do nothing if the axis position
// is unchanged; only a change in
// position fires the bindings.
if (fabs(axis_values[j] - a.last_value) > a.tolerance) {
a.last_value = axis_values[j];
for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
}
// do we have to emulate axis buttons?
a.last_dt += dt;
if(a.last_dt >= a.interval_sec) {
if (a.low.bindings[modifiers].size())
bindings[i].axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
if (a.high.bindings[modifiers].size())
bindings[i].axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
a.last_dt -= a.interval_sec;
}
}
// Fire bindings for the buttons.
for (int j = 0; j < bindings[i].nbuttons; j++) {
FGButton &b = bindings[i].buttons[j];
b.last_dt += dt;
if(b.last_dt >= b.interval_sec) {
bindings[i].buttons[j].update( modifiers, (buttons & (1 << j)) > 0 );
b.last_dt -= b.interval_sec;
}
}
}
}

View file

@ -0,0 +1,88 @@
// FGJoystickInput.hxx -- handle user input from joystick devices
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FGJOYSTICKINPUT_HXX
#define _FGJOYSTICKINPUT_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include "FGCommonInput.hxx"
#include "FGButton.hxx"
#include <simgear/structure/subsystem_mgr.hxx>
#include <plib/js.h>
////////////////////////////////////////////////////////////////////////
// The Joystick Input Class
////////////////////////////////////////////////////////////////////////
class FGJoystickInput : public SGSubsystem,FGCommonInput {
public:
FGJoystickInput();
virtual ~FGJoystickInput();
virtual void init();
virtual void postinit();
virtual void update( double dt );
static const int MAX_JOYSTICKS = 10;
static const int MAX_JOYSTICK_AXES = _JS_MAX_AXES;
static const int MAX_JOYSTICK_BUTTONS = 32;
private:
/**
* Settings for a single joystick axis.
*/
struct axis {
axis ();
virtual ~axis ();
float last_value;
float tolerance;
binding_list_t bindings[KEYMOD_MAX];
float low_threshold;
float high_threshold;
FGButton low;
FGButton high;
float interval_sec;
double last_dt;
};
/**
* Settings for a joystick.
*/
struct joystick {
joystick ();
virtual ~joystick ();
int jsnum;
jsJoystick * js;
int naxes;
int nbuttons;
axis * axes;
FGButton * buttons;
};
joystick bindings[MAX_JOYSTICKS];
};
#endif

View file

@ -0,0 +1,259 @@
// FGKeyboardInput.cxx -- handle user input from keyboard devices
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGKeyboardInput.hxx"
#include <Main/fg_props.hxx>
#include <Scripting/NasalSys.hxx>
#include <plib/pu.h>
static int getModifiers ()
{
return fgGetKeyModifiers() >> 1;
}
static bool getModShift ()
{
return (fgGetKeyModifiers() & KEYMOD_SHIFT) != 0;
}
static bool getModCtrl ()
{
return (fgGetKeyModifiers() & KEYMOD_CTRL) != 0;
}
static bool getModAlt ()
{
return (fgGetKeyModifiers() & KEYMOD_ALT) != 0;
}
static bool getModMeta ()
{
return (fgGetKeyModifiers() & KEYMOD_META) != 0;
}
static bool getModSuper ()
{
return (fgGetKeyModifiers() & KEYMOD_SUPER) != 0;
}
static bool getModHyper ()
{
return (fgGetKeyModifiers() & KEYMOD_HYPER) != 0;
}
FGKeyboardInput * FGKeyboardInput::keyboardInput = NULL;
FGKeyboardInput::FGKeyboardInput() :
_key_event(fgGetNode("/devices/status/keyboard/event", true))
{
if( keyboardInput == NULL )
keyboardInput = this;
}
FGKeyboardInput::~FGKeyboardInput()
{
if( keyboardInput == this )
keyboardInput = NULL;
}
void FGKeyboardInput::init()
{
fgRegisterKeyHandler(keyHandler);
}
void FGKeyboardInput::postinit()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing key bindings");
string module = "__kbd";
SGPropertyNode * key_nodes = fgGetNode("/input/keyboard");
if (key_nodes == NULL) {
SG_LOG(SG_INPUT, SG_WARN, "No key bindings (/input/keyboard)!!");
key_nodes = fgGetNode("/input/keyboard", true);
}
FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
vector<SGPropertyNode_ptr> nasal = key_nodes->getChildren("nasal");
for (unsigned int j = 0; j < nasal.size(); j++) {
nasal[j]->setStringValue("module", module.c_str());
nasalsys->handleCommand(nasal[j]);
}
vector<SGPropertyNode_ptr> keys = key_nodes->getChildren("key");
for (unsigned int i = 0; i < keys.size(); i++) {
int index = keys[i]->getIndex();
SG_LOG(SG_INPUT, SG_DEBUG, "Binding key " << index);
if( index >= MAX_KEYS ) {
SG_LOG(SG_INPUT, SG_WARN, "Key binding " << index << " out of range");
continue;
}
bindings[index].bindings->clear();
bindings[index].is_repeatable = keys[i]->getBoolValue("repeatable");
bindings[index].last_state = 0;
read_bindings(keys[i], bindings[index].bindings, KEYMOD_NONE, module );
}
}
void FGKeyboardInput::bind()
{
fgTie("/devices/status/keyboard", getModifiers);
fgTie("/devices/status/keyboard/shift", getModShift);
fgTie("/devices/status/keyboard/ctrl", getModCtrl);
fgTie("/devices/status/keyboard/alt", getModAlt);
fgTie("/devices/status/keyboard/meta", getModMeta);
fgTie("/devices/status/keyboard/super", getModSuper);
fgTie("/devices/status/keyboard/hyper", getModHyper);
_key_event->tie("key", SGRawValuePointer<int>(&_key_code));
_key_event->tie("pressed", SGRawValuePointer<bool>(&_key_pressed));
_key_event->tie("modifier", SGRawValuePointer<int>(&_key_modifiers));
_key_event->tie("modifier/shift", SGRawValuePointer<bool>(&_key_shift));
_key_event->tie("modifier/ctrl", SGRawValuePointer<bool>(&_key_ctrl));
_key_event->tie("modifier/alt", SGRawValuePointer<bool>(&_key_alt));
_key_event->tie("modifier/meta", SGRawValuePointer<bool>(&_key_meta));
_key_event->tie("modifier/super", SGRawValuePointer<bool>(&_key_super));
_key_event->tie("modifier/hyper", SGRawValuePointer<bool>(&_key_hyper));
}
void FGKeyboardInput::unbind()
{
fgUntie("/devices/status/keyboard");
fgUntie("/devices/status/keyboard/shift");
fgUntie("/devices/status/keyboard/ctrl");
fgUntie("/devices/status/keyboard/alt");
fgUntie("/devices/status/keyboard/meta");
fgUntie("/devices/status/keyboard/super");
fgUntie("/devices/status/keyboard/hyper");
_key_event->untie("key");
_key_event->untie("pressed");
_key_event->untie("modifier");
_key_event->untie("modifier/shift");
_key_event->untie("modifier/ctrl");
_key_event->untie("modifier/alt");
_key_event->untie("modifier/meta");
_key_event->untie("modifier/super");
_key_event->untie("modifier/hyper");
}
void FGKeyboardInput::update( double dt )
{
// nothing to do
}
const FGCommonInput::binding_list_t & FGKeyboardInput::_find_key_bindings (unsigned int k, int modifiers)
{
unsigned char kc = (unsigned char)k;
FGButton &b = bindings[k];
// Try it straight, first.
if (b.bindings[modifiers].size() > 0)
return b.bindings[modifiers];
// Alt-Gr is CTRL+ALT
else if (modifiers&(KEYMOD_CTRL|KEYMOD_ALT))
return _find_key_bindings(k, modifiers&~(KEYMOD_CTRL|KEYMOD_ALT));
// Try removing the control modifier
// for control keys.
else if ((modifiers&KEYMOD_CTRL) && iscntrl(kc))
return _find_key_bindings(k, modifiers&~KEYMOD_CTRL);
// Try removing shift modifier
// for upper case or any punctuation
// (since different keyboards will
// shift different punctuation types)
else if ((modifiers&KEYMOD_SHIFT) && (isupper(kc) || ispunct(kc)))
return _find_key_bindings(k, modifiers&~KEYMOD_SHIFT);
// Try removing alt modifier for
// high-bit characters.
else if ((modifiers&KEYMOD_ALT) && k >= 128 && k < 256)
return _find_key_bindings(k, modifiers&~KEYMOD_ALT);
// Give up and return the empty vector.
else
return b.bindings[modifiers];
}
void FGKeyboardInput::doKey (int k, int modifiers, int x, int y)
{
// Sanity check.
if (k < 0 || k >= MAX_KEYS) {
SG_LOG(SG_INPUT, SG_WARN, "Key value " << k << " out of range");
return;
}
_key_code = k;
_key_modifiers = modifiers >> 1;
_key_pressed = (modifiers & KEYMOD_RELEASED) == 0;
_key_shift = getModShift();
_key_ctrl = getModCtrl();
_key_alt = getModAlt();
_key_meta = getModMeta();
_key_super = getModSuper();
_key_hyper = getModHyper();
_key_event->fireValueChanged();
if (_key_code < 0)
return;
k = _key_code;
modifiers = _key_modifiers << 1;
if (!_key_pressed)
modifiers |= KEYMOD_RELEASED;
FGButton &b = bindings[k];
// Key pressed.
if (!(modifiers & KEYMOD_RELEASED)) {
SG_LOG( SG_INPUT, SG_DEBUG, "User pressed key " << k << " with modifiers " << modifiers );
if (!b.last_state || b.is_repeatable) {
const binding_list_t &bindings = _find_key_bindings(k, modifiers);
for (unsigned int i = 0; i < bindings.size(); i++)
bindings[i]->fire();
b.last_state = 1;
}
}
// Key released.
else {
SG_LOG(SG_INPUT, SG_DEBUG, "User released key " << k << " with modifiers " << modifiers);
if (b.last_state) {
const binding_list_t &bindings = _find_key_bindings(k, modifiers);
for (unsigned int i = 0; i < bindings.size(); i++)
bindings[i]->fire();
b.last_state = 0;
}
}
}
void FGKeyboardInput::keyHandler(int key, int keymod, int mousex, int mousey)
{
if((keymod & KEYMOD_RELEASED) == 0)
if(puKeyboard(key, PU_DOWN))
return;
if(keyboardInput)
keyboardInput->doKey(key, keymod, mousex, mousey);
}

View file

@ -0,0 +1,71 @@
// FGKeyboardInput.hxx -- handle user input from keyboard devices
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FGKEYBOARDINPUT_HXX
#define _FGKEYBOARDINPUT_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include "FGCommonInput.hxx"
#include "FGButton.hxx"
#include <simgear/structure/subsystem_mgr.hxx>
////////////////////////////////////////////////////////////////////////
// The Keyboard Input Class
////////////////////////////////////////////////////////////////////////
class FGKeyboardInput : public SGSubsystem,FGCommonInput {
public:
FGKeyboardInput();
virtual ~FGKeyboardInput();
virtual void init();
virtual void postinit();
virtual void bind();
virtual void unbind();
virtual void update( double dt );
static const int MAX_KEYS = 1024;
private:
const binding_list_t& _find_key_bindings (unsigned int k, int modifiers);
void doKey (int k, int modifiers, int x, int y);
static void keyHandler(int key, int keymod, int mousex, int mousey);
static FGKeyboardInput * keyboardInput;
FGButton bindings[MAX_KEYS];
SGPropertyNode_ptr _key_event;
int _key_code;
int _key_modifiers;
bool _key_pressed;
bool _key_shift;
bool _key_ctrl;
bool _key_alt;
bool _key_meta;
bool _key_super;
bool _key_hyper;
};
#endif

363
src/Input/FGMouseInput.cxx Normal file
View file

@ -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<SGSceneryPick> pickList;
if (FGRenderer::pick(pickList, ea)) {
std::vector<SGSceneryPick>::const_iterator i;
for (i = pickList.begin(); i != pickList.end(); ++i) {
if (i->callback->buttonPressed(b, i->info)) {
(*this)[b].push_back(i->callback);
return;
}
}
}
}
void ActivePickCallbacks::update( double dt )
{
// handle repeatable mouse press events
for( iterator mi = begin(); mi != end(); ++mi ) {
std::list<SGSharedPtr<SGPickCallback> >::iterator li;
for (li = mi->second.begin(); li != mi->second.end(); ++li) {
(*li)->update(dt);
}
}
}
#include <plib/pu.h>
#include <Model/panelnode.hxx>
#include <Cockpit/panel.hxx>
////////////////////////////////////////////////////////////////////////
// The Mouse Input Implementation
////////////////////////////////////////////////////////////////////////
const FGMouseInput::MouseCursorMap FGMouseInput::mouse_cursor_map[] = {
{ "none", MOUSE_CURSOR_NONE },
{ "inherit", MOUSE_CURSOR_POINTER },
{ "wait", MOUSE_CURSOR_WAIT },
{ "crosshair", MOUSE_CURSOR_CROSSHAIR },
{ "left-right", MOUSE_CURSOR_LEFTRIGHT },
{ 0, 0 }
};
FGMouseInput * FGMouseInput::mouseInput = NULL;
FGMouseInput::FGMouseInput()
{
if( mouseInput == NULL )
mouseInput = this;
}
FGMouseInput::~FGMouseInput()
{
if( mouseInput == this )
mouseInput = NULL;
}
void FGMouseInput::init()
{
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse bindings");
string module = "";
SGPropertyNode * mouse_nodes = fgGetNode("/input/mice");
if (mouse_nodes == 0) {
SG_LOG(SG_INPUT, SG_WARN, "No mouse bindings (/input/mice)!!");
mouse_nodes = fgGetNode("/input/mice", true);
}
int j;
for (int i = 0; i < MAX_MICE; i++) {
SGPropertyNode * mouse_node = mouse_nodes->getChild("mouse", i, true);
mouse &m = bindings[i];
// Grab node pointers
char buf[64];
sprintf(buf, "/devices/status/mice/mouse[%d]/mode", i);
m.mode_node = fgGetNode(buf);
if (m.mode_node == NULL) {
m.mode_node = fgGetNode(buf, true);
m.mode_node->setIntValue(0);
}
for (j = 0; j < MAX_MOUSE_BUTTONS; j++) {
sprintf(buf, "/devices/status/mice/mouse[%d]/button[%d]", i, j);
m.mouse_button_nodes[j] = fgGetNode(buf, true);
m.mouse_button_nodes[j]->setBoolValue(false);
}
// Read all the modes
m.nModes = mouse_node->getIntValue("mode-count", 1);
m.modes = new mouse_mode[m.nModes];
for (int j = 0; j < m.nModes; j++) {
int k;
// Read the mouse cursor for this mode
SGPropertyNode * mode_node = mouse_node->getChild("mode", j, true);
const char * cursor_name =
mode_node->getStringValue("cursor", "inherit");
m.modes[j].cursor = MOUSE_CURSOR_POINTER;
for (k = 0; mouse_cursor_map[k].name != 0; k++) {
if (!strcmp(mouse_cursor_map[k].name, cursor_name)) {
m.modes[j].cursor = mouse_cursor_map[k].cursor;
break;
}
}
// Read other properties for this mode
m.modes[j].constrained = mode_node->getBoolValue("constrained", false);
m.modes[j].pass_through = mode_node->getBoolValue("pass-through", false);
// Read the button bindings for this mode
m.modes[j].buttons = new FGButton[MAX_MOUSE_BUTTONS];
char buf[32];
for (k = 0; k < MAX_MOUSE_BUTTONS; k++) {
sprintf(buf, "mouse button %d", k);
SG_LOG(SG_INPUT, SG_DEBUG, "Initializing mouse button " << k);
m.modes[j].buttons[k].init( mode_node->getChild("button", k), buf, module );
}
// Read the axis bindings for this mode
read_bindings(mode_node->getChild("x-axis", 0, true), m.modes[j].x_bindings, KEYMOD_NONE, module );
read_bindings(mode_node->getChild("y-axis", 0, true), m.modes[j].y_bindings, KEYMOD_NONE, module );
}
}
fgRegisterMouseClickHandler(mouseClickHandler);
fgRegisterMouseMotionHandler(mouseMotionHandler);
}
void FGMouseInput::update ( double dt )
{
mouse &m = bindings[0];
int mode = m.mode_node->getIntValue();
if (mode != m.current_mode) {
m.current_mode = mode;
m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
if (mode >= 0 && mode < m.nModes) {
fgSetMouseCursor(m.modes[mode].cursor);
m.x = fgGetInt("/sim/startup/xsize", 800) / 2;
m.y = fgGetInt("/sim/startup/ysize", 600) / 2;
fgWarpMouse(m.x, m.y);
} else {
SG_LOG(SG_INPUT, SG_DEBUG, "Mouse mode " << mode << " out of range");
fgSetMouseCursor(MOUSE_CURSOR_POINTER);
}
}
if ( fgGetBool( "/sim/mouse/hide-cursor", true ) ) {
if ( m.x != m.save_x || m.y != m.save_y ) {
m.timeout = fgGetDouble( "/sim/mouse/cursor-timeout-sec", 10.0 );
if (fgGetMouseCursor() == MOUSE_CURSOR_NONE)
fgSetMouseCursor(m.modes[mode].cursor);
} else {
m.timeout -= dt;
if ( m.timeout <= 0.0 ) {
fgSetMouseCursor(MOUSE_CURSOR_NONE);
m.timeout = 0.0;
}
}
m.save_x = m.x;
m.save_y = m.y;
}
activePickCallbacks.update( dt );
}
FGMouseInput::mouse::mouse ()
: x(-1),
y(-1),
save_x(-1),
save_y(-1),
nModes(1),
current_mode(0),
modes(NULL)
{
}
FGMouseInput::mouse::~mouse ()
{
delete [] modes;
}
FGMouseInput::mouse_mode::mouse_mode ()
: cursor(MOUSE_CURSOR_POINTER),
constrained(false),
pass_through(false),
buttons(NULL)
{
}
FGMouseInput::mouse_mode::~mouse_mode ()
{
// FIXME: memory leak
// for (int i = 0; i < KEYMOD_MAX; i++) {
// int j;
// for (j = 0; i < x_bindings[i].size(); j++)
// delete bindings[i][j];
// for (j = 0; j < y_bindings[i].size(); j++)
// delete bindings[i][j];
// }
delete [] buttons;
}
void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
{
int modifiers = fgGetKeyModifiers();
mouse &m = bindings[0];
mouse_mode &mode = m.modes[m.current_mode];
// Let the property manager know.
if (b >= 0 && b < MAX_MOUSE_BUTTONS)
m.mouse_button_nodes[b]->setBoolValue(updown == MOUSE_BUTTON_DOWN);
// Pass on to PUI and the panel if
// requested, and return if one of
// them consumes the event.
if (updown != MOUSE_BUTTON_DOWN) {
// Execute the mouse up event in any case, may be we should
// stop processing here?
while (!activePickCallbacks[b].empty()) {
activePickCallbacks[b].front()->buttonReleased();
activePickCallbacks[b].pop_front();
}
}
if (mode.pass_through) {
if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
return;
else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
globals->get_current_panel()->getVisibility() &&
globals->get_current_panel()->doMouseAction(b, updown, x, y))
return;
else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
return;
else {
// pui didn't want the click event so compute a
// scenegraph intersection point corresponding to the mouse click
if (updown == MOUSE_BUTTON_DOWN) {
activePickCallbacks.init( b, ea );
}
}
}
// OK, PUI and the panel didn't want the click
if (b >= MAX_MOUSE_BUTTONS) {
SG_LOG(SG_INPUT, SG_ALERT, "Mouse button " << b
<< " where only " << MAX_MOUSE_BUTTONS << " expected");
return;
}
m.modes[m.current_mode].buttons[b].update( modifiers, 0 != updown, x, y);
}
void FGMouseInput::doMouseMotion (int x, int y)
{
// Don't call fgGetKeyModifiers() here, until we are using a
// toolkit that supports getting the mods from outside a key
// callback. Glut doesn't.
int modifiers = KEYMOD_NONE;
int xsize = fgGetInt("/sim/startup/xsize", 800);
int ysize = fgGetInt("/sim/startup/ysize", 600);
mouse &m = bindings[0];
if (m.current_mode < 0 || m.current_mode >= m.nModes) {
m.x = x;
m.y = y;
return;
}
mouse_mode &mode = m.modes[m.current_mode];
// Pass on to PUI if requested, and return
// if PUI consumed the event.
if (mode.pass_through && puMouse(x, y)) {
m.x = x;
m.y = y;
return;
}
// OK, PUI didn't want the event,
// so we can play with it.
if (x != m.x) {
int delta = x - m.x;
for (unsigned int i = 0; i < mode.x_bindings[modifiers].size(); i++)
mode.x_bindings[modifiers][i]->fire(double(delta), double(xsize));
}
if (y != m.y) {
int delta = y - m.y;
for (unsigned int i = 0; i < mode.y_bindings[modifiers].size(); i++)
mode.y_bindings[modifiers][i]->fire(double(delta), double(ysize));
}
// Constrain the mouse if requested
if (mode.constrained) {
bool need_warp = false;
if (x <= (xsize * .25) || x >= (xsize * .75)) {
x = int(xsize * .5);
need_warp = true;
}
if (y <= (ysize * .25) || y >= (ysize * .75)) {
y = int(ysize * .5);
need_warp = true;
}
if (need_warp)
fgWarpMouse(x, y);
}
if (m.x != x)
fgSetInt("/devices/status/mice/mouse/x", m.x = x);
if (m.y != y)
fgSetInt("/devices/status/mice/mouse/y", m.y = y);
}
void FGMouseInput::mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea)
{
if(mouseInput)
mouseInput->doMouseClick(button, updown, x, y, mainWindow, ea);
}
void FGMouseInput::mouseMotionHandler(int x, int y)
{
if (mouseInput != 0)
mouseInput->doMouseMotion(x, y);
}

123
src/Input/FGMouseInput.hxx Normal file
View file

@ -0,0 +1,123 @@
// FGMouseInput.hxx -- handle user input from mouse devices
//
// Written by Torsten Dreyer, started August 2009
// Based on work from David Megginson, started May 2001.
//
// Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
// Copyright (C) 2001 David Megginson, david@megginson.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _FGMOUSEINPUT_HXX
#define _FGMOUSEINPUT_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include "FGCommonInput.hxx"
#include "FGButton.hxx"
#include <map>
#include <list>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/scene/util/SGPickCallback.hxx>
#include <Main/renderer.hxx>
/**
* List of currently pressed mouse button events
*/
class ActivePickCallbacks : public std::map<int, std::list<SGSharedPtr<SGPickCallback> > > {
public:
void update( double dt );
void init( int b, const osgGA::GUIEventAdapter* ea );
};
////////////////////////////////////////////////////////////////////////
// The Mouse Input Class
////////////////////////////////////////////////////////////////////////
class FGMouseInput : public SGSubsystem,FGCommonInput {
public:
FGMouseInput();
virtual ~FGMouseInput();
virtual void init();
virtual void update( double dt );
static const int MAX_MICE = 1;
static const int MAX_MOUSE_BUTTONS = 8;
private:
void doMouseClick (int b, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter* ea);
void doMouseMotion (int x, int y);
static FGMouseInput * mouseInput;
static void mouseClickHandler(int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
static void mouseMotionHandler(int x, int y);
ActivePickCallbacks activePickCallbacks;
/**
* Settings for a mouse mode.
*/
struct mouse_mode {
mouse_mode ();
virtual ~mouse_mode ();
int cursor;
bool constrained;
bool pass_through;
FGButton * buttons;
binding_list_t x_bindings[KEYMOD_MAX];
binding_list_t y_bindings[KEYMOD_MAX];
};
/**
* Settings for a mouse.
*/
struct mouse {
mouse ();
virtual ~mouse ();
int x;
int y;
int save_x;
int save_y;
SGPropertyNode_ptr mode_node;
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
int nModes;
int current_mode;
double timeout;
mouse_mode * modes;
};
//
// Map of all known cursor names
// This used to contain all the Glut cursors, but those are
// not defined by other toolkits. It now supports only the cursor
// images we actually use, in the interest of portability. Someday,
// it would be cool to write an OpenGL cursor renderer, with the
// cursors defined as textures referenced in the property tree. This
// list could then be eliminated. -Andy
//
const static struct MouseCursorMap {
const char * name;
int cursor;
} mouse_cursor_map[];
mouse bindings[MAX_MICE];
};
#endif

View file

@ -2,7 +2,12 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkgdatadir)\"
noinst_LIBRARIES = libInput.a 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 bin_PROGRAMS = js_demo fgjs

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,10 @@
// input.hxx -- handle user input from various sources. // input.hxx -- handle user input from various sources.
// //
// Written by David Megginson, started May 2001. // 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) 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 // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as // modify it under the terms of the GNU General Public License as
@ -28,40 +30,10 @@
# error This library requires C++ # error This library requires C++
#endif #endif
#include <plib/js.h>
#include <plib/ul.h>
#include <simgear/compiler.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/structure/subsystem_mgr.hxx> #include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/structure/SGBinding.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/util/SGSceneUserData.hxx>
#include <Main/fg_os.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <map>
#include <list>
#include <vector>
using std::map;
using std::vector;
#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 * keyboard, joystick, mouse, or even panel switches -- in a consistent
* way, and to allow users to rebind any of the actions at runtime.</p> * way, and to allow users to rebind any of the actions at runtime.</p>
*/ */
class FGInput : public SGSubsystem class FGInput : public SGSubsystemGroup
{ {
public: public:
/** /**
@ -89,259 +61,6 @@ public:
*/ */
virtual ~FGInput(); virtual ~FGInput();
//
// Implementation of SGSubsystem.
//
virtual void init ();
virtual void reinit ();
virtual void postinit ();
virtual void bind ();
virtual void unbind ();
virtual void update (double dt);
virtual void suspend ();
virtual void resume ();
virtual bool is_suspended () const;
/**
* Control whether this is the default module to receive events.
*
* The first input module created will set itself as the default
* automatically.
*
* @param status true if this should be the default module for
* events, false otherwise.
*/
virtual void makeDefault (bool status = true);
/**
* Handle a single keystroke.
*
* @param k The integer key code, see Main/fg_os.hxx
* @param modifiers Modifier keys pressed (bitfield).
* @param x The mouse x position at the time of keypress.
* @param y The mouse y position at the time of keypress.
*/
virtual void doKey (int k, int modifiers, int x, int y);
/**
* Handle a mouse click event.
*
* @param button The mouse button selected.
* @param updown Button status.
* @param x The X position of the mouse event, in screen coordinates.
* @param y The Y position of the mouse event, in screen coordinates.
*/
virtual void doMouseClick (int button, int updown, int x, int y, bool mainWindow, const osgGA::GUIEventAdapter*);
/**
* Handle mouse motion.
*
* @param x The new mouse x position, in screen coordinates.
* @param y The new mouse y position, in screen coordinates.
*/
virtual void doMouseMotion (int x, int y);
private:
// Constants
enum
{
MAX_KEYS = 1024,
MAX_JOYSTICKS = 10,
MAX_JOYSTICK_AXES = _JS_MAX_AXES,
MAX_JOYSTICK_BUTTONS = 32,
MAX_MICE = 1,
MAX_MOUSE_BUTTONS = 8
};
struct mouse;
friend struct mouse;
typedef vector<SGSharedPtr<SGBinding> > binding_list_t;
/**
* Settings for a key or button.
*/
struct button {
button ();
virtual ~button ();
bool is_repeatable;
float interval_sec;
float last_dt;
int last_state;
binding_list_t bindings[KEYMOD_MAX];
};
/**
* Settings for a single joystick axis.
*/
struct axis {
axis ();
virtual ~axis ();
float last_value;
float tolerance;
binding_list_t bindings[KEYMOD_MAX];
float low_threshold;
float high_threshold;
struct button low;
struct button high;
float interval_sec;
double last_dt;
};
/**
* Settings for a joystick.
*/
struct joystick {
joystick ();
virtual ~joystick ();
int jsnum;
jsJoystick * js;
int naxes;
int nbuttons;
axis * axes;
button * buttons;
};
/**
* Settings for a mouse mode.
*/
struct mouse_mode {
mouse_mode ();
virtual ~mouse_mode ();
int cursor;
bool constrained;
bool pass_through;
button * buttons;
binding_list_t x_bindings[KEYMOD_MAX];
binding_list_t y_bindings[KEYMOD_MAX];
};
/**
* Settings for a mouse.
*/
struct mouse {
mouse ();
virtual ~mouse ();
int x;
int y;
int save_x;
int save_y;
SGPropertyNode_ptr mode_node;
SGPropertyNode_ptr mouse_button_nodes[MAX_MOUSE_BUTTONS];
int nModes;
int current_mode;
double timeout;
mouse_mode * modes;
};
/**
* Initialize joystick bindings.
*/
void _init_joystick ();
/**
* Scan directory recursively for "named joystick" configuration files,
* and read them into /input/joysticks/js-named[index]++.
*/
void _scan_joystick_dir (SGPath *path, SGPropertyNode* node, int *index);
/**
* Initialize mouse bindings.
*/
void _init_mouse ();
/**
* Initialize a single button.
*/
inline void _init_button (const SGPropertyNode * node,
button &b,
const string name);
/**
* Initialize key bindings, as well as those joystick parts that
* depend on a working Nasal subsystem.
*/
void _postinit_keyboard ();
void _postinit_joystick ();
/**
* Update the keyboard.
*/
void _update_keyboard ();
/**
* Update the joystick.
*/
void _update_joystick (double dt);
/**
* Update the mouse.
*/
void _update_mouse (double dt);
/**
* Update a single button.
*/
inline void _update_button (button &b, int modifiers, bool pressed,
int x, int y);
/**
* Read bindings and modifiers.
*/
void _read_bindings (const SGPropertyNode * node,
binding_list_t * binding_list,
int modifiers);
/**
* Look up the bindings for a key code.
*/
const binding_list_t& _find_key_bindings (unsigned int k,
int modifiers);
button _key_bindings[MAX_KEYS];
joystick _joystick_bindings[MAX_JOYSTICKS];
mouse _mouse_bindings[MAX_MICE];
/**
* Nasal module name/namespace.
*/
string _module;
/**
* List of currently pressed mouse button events
*/
std::map<int, std::list<SGSharedPtr<SGPickCallback> > > _activePickCallbacks;
/**
* Key listener interface.
*/
SGPropertyNode_ptr _key_event;
int _key_code;
int _key_modifiers;
bool _key_pressed;
bool _key_shift;
bool _key_ctrl;
bool _key_alt;
bool _key_meta;
bool _key_super;
bool _key_hyper;
}; };
#endif // _INPUT_HXX #endif // _INPUT_HXX