391 lines
11 KiB
C++
391 lines
11 KiB
C++
// 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$
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <cstring>
|
|
#include "FGEventInput.hxx"
|
|
#include <Main/fg_props.hxx>
|
|
#include <simgear/io/sg_file.hxx>
|
|
#include <simgear/props/props_io.hxx>
|
|
#include <simgear/math/SGMath.hxx>
|
|
#include <Scripting/NasalSys.hxx>
|
|
|
|
using simgear::PropertyList;
|
|
using std::cout;
|
|
using std::endl;
|
|
using std::map;
|
|
using std::string;
|
|
|
|
FGEventSetting::FGEventSetting( SGPropertyNode_ptr base ) :
|
|
value(0.0)
|
|
{
|
|
SGPropertyNode_ptr n;
|
|
|
|
if( (n = base->getNode( "value" )) != NULL ) {
|
|
valueNode = NULL;
|
|
value = n->getDoubleValue();
|
|
} else {
|
|
n = base->getNode( "property" );
|
|
if( n == NULL ) {
|
|
SG_LOG( SG_INPUT, SG_WARN, "Neither <value> nor <property> defined for event setting." );
|
|
} else {
|
|
valueNode = fgGetNode( n->getStringValue(), true );
|
|
}
|
|
}
|
|
|
|
if( (n = base->getChild("condition")) != NULL )
|
|
condition = sgReadCondition(base, n);
|
|
else
|
|
SG_LOG( SG_INPUT, SG_ALERT, "No condition for event setting." );
|
|
}
|
|
|
|
double FGEventSetting::GetValue()
|
|
{
|
|
return valueNode == NULL ? value : valueNode->getDoubleValue();
|
|
}
|
|
|
|
bool FGEventSetting::Test()
|
|
{
|
|
return condition == NULL ? true : condition->test();
|
|
}
|
|
|
|
static inline bool StartsWith( string & s, const char * cp )
|
|
{
|
|
return s.find( cp ) == 0;
|
|
}
|
|
|
|
FGInputEvent * FGInputEvent::NewObject( FGInputDevice * device, SGPropertyNode_ptr node )
|
|
{
|
|
string name = node->getStringValue( "name", "" );
|
|
if( StartsWith( name, "button-" ) )
|
|
return new FGButtonEvent( device, node );
|
|
|
|
if( StartsWith( name, "rel-" ) )
|
|
return new FGRelAxisEvent( device, node );
|
|
|
|
if( StartsWith( name, "abs-" ) )
|
|
return new FGAbsAxisEvent( device, node );
|
|
|
|
return new FGInputEvent( device, node );
|
|
}
|
|
|
|
FGInputEvent::FGInputEvent( FGInputDevice * aDevice, SGPropertyNode_ptr node ) :
|
|
device( aDevice ),
|
|
lastDt(0.0),
|
|
lastSettingValue(std::numeric_limits<float>::quiet_NaN())
|
|
{
|
|
name = node->getStringValue( "name", "" );
|
|
desc = node->getStringValue( "desc", "" );
|
|
intervalSec = node->getDoubleValue("interval-sec",0.0);
|
|
|
|
read_bindings( node, bindings, KEYMOD_NONE, device->GetNasalModule() );
|
|
|
|
PropertyList settingNodes = node->getChildren("setting");
|
|
for( PropertyList::iterator it = settingNodes.begin(); it != settingNodes.end(); ++it )
|
|
settings.push_back( new FGEventSetting( *it ) );
|
|
}
|
|
|
|
FGInputEvent::~FGInputEvent()
|
|
{
|
|
}
|
|
|
|
void FGInputEvent::update( double dt )
|
|
{
|
|
for( setting_list_t::iterator it = settings.begin(); it != settings.end(); ++it ) {
|
|
if( (*it)->Test() ) {
|
|
double value = (*it)->GetValue();
|
|
if( value != lastSettingValue ) {
|
|
device->Send( GetName(), (*it)->GetValue() );
|
|
lastSettingValue = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FGInputEvent::fire( FGEventData & eventData )
|
|
{
|
|
lastDt += eventData.dt;
|
|
if( lastDt >= intervalSec ) {
|
|
|
|
for( binding_list_t::iterator it = bindings[eventData.modifiers].begin(); it != bindings[eventData.modifiers].end(); ++it )
|
|
fire( *it, eventData );
|
|
|
|
lastDt -= intervalSec;
|
|
}
|
|
}
|
|
|
|
void FGInputEvent::fire( SGBinding * binding, FGEventData & eventData )
|
|
{
|
|
binding->fire();
|
|
}
|
|
|
|
|
|
|
|
FGAxisEvent::FGAxisEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
|
|
FGInputEvent( device, node )
|
|
{
|
|
tolerance = node->getDoubleValue("tolerance", 0.002);
|
|
minRange = node->getDoubleValue("min-range", 0.0 );
|
|
maxRange = node->getDoubleValue("max-range", 0.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;
|
|
|
|
// We need a copy of the FGEventData struct to set the new value and to avoid side effects
|
|
FGEventData ed = eventData;
|
|
|
|
if( fabs(ed.value) < deadband )
|
|
ed.value = 0.0;
|
|
|
|
if( minRange != maxRange )
|
|
ed.value = 2.0*(eventData.value-minRange)/(maxRange-minRange)-1.0;
|
|
|
|
FGInputEvent::fire( ed );
|
|
}
|
|
|
|
void FGAbsAxisEvent::fire( SGBinding * binding, FGEventData & eventData )
|
|
{
|
|
// sets the "setting" node
|
|
binding->fire( eventData.value );
|
|
}
|
|
|
|
FGRelAxisEvent::FGRelAxisEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
|
|
FGAxisEvent( device, node )
|
|
{
|
|
// relative axes can't use tolerance
|
|
tolerance = 0.0;
|
|
}
|
|
|
|
void FGRelAxisEvent::fire( SGBinding * binding, FGEventData & eventData )
|
|
{
|
|
// sets the "offset" node
|
|
binding->fire( eventData.value, 1.0 );
|
|
}
|
|
|
|
FGButtonEvent::FGButtonEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
|
|
FGInputEvent( device, node ),
|
|
repeatable(false),
|
|
lastState(false)
|
|
{
|
|
repeatable = node->getBoolValue("repeatable", repeatable);
|
|
}
|
|
|
|
void FGButtonEvent::fire( FGEventData & eventData )
|
|
{
|
|
bool pressed = eventData.value > 0.0;
|
|
if (pressed) {
|
|
// The press event may be repeated.
|
|
if (!lastState || repeatable) {
|
|
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been pressed" );
|
|
FGInputEvent::fire( eventData );
|
|
}
|
|
} else {
|
|
// The release event is never repeated.
|
|
if (lastState) {
|
|
SG_LOG( SG_INPUT, SG_DEBUG, "Button has been released" );
|
|
eventData.modifiers|=KEYMOD_RELEASED;
|
|
FGInputEvent::fire( eventData );
|
|
}
|
|
}
|
|
|
|
lastState = pressed;
|
|
}
|
|
|
|
FGInputDevice::~FGInputDevice()
|
|
{
|
|
FGNasalSys *nas = (FGNasalSys *)globals->get_subsystem("nasal");
|
|
if (nas && deviceNode ) {
|
|
SGPropertyNode_ptr nasal = deviceNode->getNode("nasal");
|
|
if( nasal ) {
|
|
SGPropertyNode_ptr nasalClose = nasal->getNode("close");
|
|
if (nasalClose) {
|
|
const string s = nasalClose->getStringValue();
|
|
nas->createModule(nasalModule.c_str(), nasalModule.c_str(), s.c_str(), s.length(), deviceNode );
|
|
}
|
|
}
|
|
nas->deleteModule(nasalModule.c_str());
|
|
}
|
|
}
|
|
|
|
void FGInputDevice::Configure( SGPropertyNode_ptr aDeviceNode )
|
|
{
|
|
deviceNode = aDeviceNode;
|
|
|
|
nasalModule = string("__event:") + GetName();
|
|
|
|
PropertyList eventNodes = deviceNode->getChildren( "event" );
|
|
for( PropertyList::iterator it = eventNodes.begin(); it != eventNodes.end(); ++it )
|
|
AddHandledEvent( FGInputEvent::NewObject( this, *it ) );
|
|
|
|
debugEvents = deviceNode->getBoolValue("debug-events", debugEvents );
|
|
grab = deviceNode->getBoolValue("grab", grab );
|
|
|
|
// TODO:
|
|
// add nodes for the last event:
|
|
// last-event/name [string]
|
|
// last-event/value [double]
|
|
|
|
SGPropertyNode_ptr nasal = deviceNode->getNode("nasal");
|
|
if (nasal) {
|
|
SGPropertyNode_ptr open = nasal->getNode("open");
|
|
if (open) {
|
|
const string s = open->getStringValue();
|
|
FGNasalSys *nas = (FGNasalSys *)globals->get_subsystem("nasal");
|
|
if (nas)
|
|
nas->createModule(nasalModule.c_str(), nasalModule.c_str(), s.c_str(), s.length(), deviceNode );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void FGInputDevice::update( double dt )
|
|
{
|
|
for( map<string,FGInputEvent_ptr>::iterator it = handledEvents.begin(); it != handledEvents.end(); it++ )
|
|
(*it).second->update( dt );
|
|
}
|
|
|
|
void FGInputDevice::HandleEvent( FGEventData & eventData )
|
|
{
|
|
string eventName = TranslateEventName( eventData );
|
|
if( debugEvents )
|
|
cout << GetName() << " has event " <<
|
|
eventName << " modifiers=" << eventData.modifiers << " value=" << eventData.value << 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::update( double dt )
|
|
{
|
|
// call each associated device's update() method
|
|
for( map<int,FGInputDevice*>::iterator it = input_devices.begin(); it != input_devices.end(); ++it )
|
|
(*it).second->update( dt );
|
|
}
|
|
|
|
unsigned FGEventInput::AddDevice( FGInputDevice * inputDevice )
|
|
{
|
|
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
|
|
SGPropertyNode_ptr deviceNode = NULL;
|
|
|
|
// look for configuration in the device map
|
|
if ( configMap.hasConfiguration( inputDevice->GetName() ) ) {
|
|
// found - copy to /input/event/device[n]
|
|
|
|
// find a free index
|
|
unsigned index;
|
|
for( index = 0; index < MAX_DEVICES; index++ )
|
|
if( (deviceNode = baseNode->getNode( "device", index, false ) ) == NULL )
|
|
break;
|
|
|
|
if( index == MAX_DEVICES ) {
|
|
SG_LOG(SG_INPUT, SG_WARN, "To many event devices - ignoring " << inputDevice->GetName() );
|
|
return INVALID_DEVICE_INDEX;
|
|
}
|
|
|
|
// create this node
|
|
deviceNode = baseNode->getNode( "device", index, true );
|
|
|
|
// and copy the properties from the configuration tree
|
|
copyProperties( configMap.configurationForDeviceName(inputDevice->GetName()), deviceNode );
|
|
|
|
}
|
|
|
|
if( deviceNode == NULL ) {
|
|
SG_LOG(SG_INPUT, SG_DEBUG, "No configuration found for device " << inputDevice->GetName() );
|
|
delete inputDevice;
|
|
return INVALID_DEVICE_INDEX;
|
|
}
|
|
|
|
inputDevice->Configure( deviceNode );
|
|
|
|
try {
|
|
inputDevice->Open();
|
|
input_devices[ deviceNode->getIndex() ] = inputDevice;
|
|
}
|
|
catch( ... ) {
|
|
delete inputDevice;
|
|
SG_LOG(SG_INPUT, SG_ALERT, "can't open InputDevice " << inputDevice->GetName() );
|
|
return INVALID_DEVICE_INDEX;
|
|
}
|
|
|
|
SG_LOG(SG_INPUT, SG_DEBUG, "using InputDevice " << inputDevice->GetName() );
|
|
return deviceNode->getIndex();
|
|
}
|
|
|
|
void FGEventInput::RemoveDevice( unsigned index )
|
|
{
|
|
// not fully implemented yet
|
|
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
|
|
SGPropertyNode_ptr deviceNode = NULL;
|
|
|
|
FGInputDevice *inputDevice = input_devices[index];
|
|
if (inputDevice) {
|
|
input_devices.erase(index);
|
|
delete inputDevice;
|
|
|
|
}
|
|
deviceNode = baseNode->removeChild("device", index);
|
|
}
|
|
|