Advanced input subsystem - Step3: Adding support for the Linux event devices
This commit is contained in:
parent
3bce312213
commit
5c57153e62
8 changed files with 738 additions and 36 deletions
14
configure.ac
14
configure.ac
|
@ -112,6 +112,14 @@ fi
|
||||||
AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno")
|
AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno")
|
||||||
|
|
||||||
|
|
||||||
|
dnl EXPERIMENTAL generic event driven input device
|
||||||
|
# defaults to no
|
||||||
|
AC_ARG_WITH(eventinput, [ --with-eventinput Include event driven input (EXPERIMENTAL) [default=no]], [], [with_eventinput=no])
|
||||||
|
if test "x$with_eventinput" = "xyes"; then
|
||||||
|
AC_DEFINE([WITH_EVENTINPUT], 1, [Define to enable generic event driven input device])
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL(WITH_EVENTINPUT, test "x$with_eventinput" = "xyes")
|
||||||
|
|
||||||
dnl Thread related checks
|
dnl Thread related checks
|
||||||
# defaults to yes
|
# defaults to yes
|
||||||
AC_ARG_WITH(threads, [ --with-threads Include tile loading threads [default=yes]], [], [with_threads=yes])
|
AC_ARG_WITH(threads, [ --with-threads Include tile loading threads [default=yes]], [], [with_threads=yes])
|
||||||
|
@ -760,6 +768,12 @@ else
|
||||||
echo "threads: no"
|
echo "threads: no"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "x$with_eventinput" = "xyes"; then
|
||||||
|
echo "event input: yes"
|
||||||
|
else
|
||||||
|
echo "event input: no"
|
||||||
|
fi
|
||||||
|
|
||||||
if test "x$enable_sp_fdms" != "xno"; then
|
if test "x$enable_sp_fdms" != "xno"; then
|
||||||
echo "Include special purpose flight models: yes"
|
echo "Include special purpose flight models: yes"
|
||||||
else
|
else
|
||||||
|
|
|
@ -20,60 +20,117 @@
|
||||||
//
|
//
|
||||||
// $Id$
|
// $Id$
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "FGEventInput.hxx"
|
#include "FGEventInput.hxx"
|
||||||
#include <Main/fg_props.hxx>
|
#include <Main/fg_props.hxx>
|
||||||
#include <simgear/io/sg_file.hxx>
|
#include <simgear/io/sg_file.hxx>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
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 )
|
static inline bool StartsWith( string & s, const char * cp )
|
||||||
{
|
{
|
||||||
return s.compare( 0, strlen(cp), cp ) == 0;
|
return s.compare( 0, strlen(cp), cp ) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FGInputEvent * FGInputEvent::NewObject( SGPropertyNode_ptr node )
|
FGInputEvent * FGInputEvent::NewObject( FGInputDevice * device, SGPropertyNode_ptr node )
|
||||||
{
|
{
|
||||||
string name = node->getStringValue( "name" );
|
string name = node->getStringValue( "name", "" );
|
||||||
if( StartsWith( name, "button-" ) )
|
if( StartsWith( name, "button-" ) )
|
||||||
return new FGButtonEvent( node );
|
return new FGButtonEvent( device, node );
|
||||||
|
|
||||||
if( StartsWith( name, "rel-" ) )
|
if( StartsWith( name, "rel-" ) )
|
||||||
return new FGAxisEvent( node );
|
return new FGAxisEvent( device, node );
|
||||||
|
|
||||||
if( StartsWith( name, "abs-" ) )
|
if( StartsWith( name, "abs-" ) )
|
||||||
return new FGAxisEvent( node );
|
return new FGAxisEvent( device, node );
|
||||||
|
|
||||||
return NULL;
|
return new FGInputEvent( device, node );
|
||||||
}
|
}
|
||||||
|
|
||||||
FGInputEvent::FGInputEvent( SGPropertyNode_ptr node ) :
|
FGInputEvent::FGInputEvent( FGInputDevice * aDevice, SGPropertyNode_ptr node ) :
|
||||||
lastDt(0.0)
|
device( aDevice ),
|
||||||
|
lastDt(0.0),
|
||||||
|
lastSettingValue(std::numeric_limits<float>::quiet_NaN())
|
||||||
{
|
{
|
||||||
name = node->getStringValue( "name" );
|
name = node->getStringValue( "name", "" );
|
||||||
desc = node->getStringValue( "desc" );
|
desc = node->getStringValue( "desc", "" );
|
||||||
intervalSec = node->getDoubleValue("interval-sec",0.0);
|
intervalSec = node->getDoubleValue("interval-sec",0.0);
|
||||||
string module = "event";
|
string module = "event";
|
||||||
|
|
||||||
read_bindings( node, bindings, KEYMOD_NONE, module );
|
read_bindings( node, bindings, KEYMOD_NONE, module );
|
||||||
|
|
||||||
|
vector<SGPropertyNode_ptr> settingNodes = node->getChildren("setting");
|
||||||
|
for( vector<SGPropertyNode_ptr>::iterator it = settingNodes.begin(); it != settingNodes.end(); it++ )
|
||||||
|
settings.push_back( new FGEventSetting( *it ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
FGInputEvent::~FGInputEvent()
|
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 )
|
void FGInputEvent::fire( FGEventData & eventData )
|
||||||
{
|
{
|
||||||
lastDt += eventData.dt;
|
lastDt += eventData.dt;
|
||||||
if( lastDt >= intervalSec ) {
|
if( lastDt >= intervalSec ) {
|
||||||
|
|
||||||
for( binding_list_t::iterator it = bindings[KEYMOD_NONE].begin(); it != bindings[KEYMOD_NONE].end(); it++ )
|
for( binding_list_t::iterator it = bindings[eventData.modifiers].begin(); it != bindings[eventData.modifiers].end(); it++ )
|
||||||
(*it)->fire( eventData.value, 1.0 );
|
(*it)->fire( eventData.value, 1.0 );
|
||||||
|
|
||||||
lastDt -= intervalSec;
|
lastDt -= intervalSec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FGAxisEvent::FGAxisEvent( SGPropertyNode_ptr node ) :
|
FGAxisEvent::FGAxisEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
|
||||||
FGInputEvent( node )
|
FGInputEvent( device, node )
|
||||||
{
|
{
|
||||||
tolerance = node->getDoubleValue("tolerance", 0.002);
|
tolerance = node->getDoubleValue("tolerance", 0.002);
|
||||||
minRange = node->getDoubleValue("min-range", -1024.0);
|
minRange = node->getDoubleValue("min-range", -1024.0);
|
||||||
|
@ -93,24 +150,49 @@ void FGAxisEvent::fire( FGEventData & eventData )
|
||||||
FGInputEvent::fire( eventData );
|
FGInputEvent::fire( eventData );
|
||||||
}
|
}
|
||||||
|
|
||||||
FGButtonEvent::FGButtonEvent( SGPropertyNode_ptr node ) :
|
FGButtonEvent::FGButtonEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
|
||||||
FGInputEvent( node )
|
FGInputEvent( device, node ),
|
||||||
|
lastState(false),
|
||||||
|
repeatable(false)
|
||||||
{
|
{
|
||||||
|
repeatable = node->getBoolValue("repeatable", repeatable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGButtonEvent::fire( FGEventData & eventData )
|
void FGButtonEvent::fire( FGEventData & eventData )
|
||||||
{
|
{
|
||||||
FGInputEvent::fire( 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()
|
FGInputDevice::~FGInputDevice()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 )
|
void FGInputDevice::HandleEvent( FGEventData & eventData )
|
||||||
{
|
{
|
||||||
string eventName = TranslateEventName( eventData );
|
string eventName = TranslateEventName( eventData );
|
||||||
cout << GetName() << " has event " << eventName << endl;
|
// cout << GetName() << " has event " << eventName << " modifiers=" << eventData.modifiers << " value=" << eventData.value << endl;
|
||||||
if( handledEvents.count( eventName ) > 0 ) {
|
if( handledEvents.count( eventName ) > 0 ) {
|
||||||
handledEvents[ eventName ]->fire( eventData );
|
handledEvents[ eventName ]->fire( eventData );
|
||||||
}
|
}
|
||||||
|
@ -146,6 +228,13 @@ 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 );
|
||||||
|
}
|
||||||
|
|
||||||
void FGEventInput::AddDevice( FGInputDevice * inputDevice )
|
void FGEventInput::AddDevice( FGInputDevice * inputDevice )
|
||||||
{
|
{
|
||||||
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
|
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
|
||||||
|
@ -174,19 +263,14 @@ void FGEventInput::AddDevice( FGInputDevice * inputDevice )
|
||||||
}
|
}
|
||||||
|
|
||||||
if( deviceNode == NULL ) {
|
if( deviceNode == NULL ) {
|
||||||
SG_LOG(SG_INPUT, SG_WARN, "No configuration found for device " << inputDevice->GetName() );
|
SG_LOG(SG_INPUT, SG_DEBUG, "No configuration found for device " << inputDevice->GetName() );
|
||||||
|
delete inputDevice;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<SGPropertyNode_ptr> eventNodes = deviceNode->getChildren( "event" );
|
vector<SGPropertyNode_ptr> eventNodes = deviceNode->getChildren( "event" );
|
||||||
for( vector<SGPropertyNode_ptr>::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ ) {
|
for( vector<SGPropertyNode_ptr>::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ )
|
||||||
FGInputEvent * p = FGInputEvent::NewObject( *it );
|
inputDevice->AddHandledEvent( FGInputEvent::NewObject( inputDevice, *it ) );
|
||||||
if( p == NULL ) {
|
|
||||||
SG_LOG(SG_INPUT, SG_WARN, "Unhandled event/name in " << inputDevice->GetName() );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
inputDevice->AddHandledEvent( p );
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// add nodes for the last event:
|
// add nodes for the last event:
|
||||||
|
@ -198,6 +282,9 @@ void FGEventInput::AddDevice( FGInputDevice * inputDevice )
|
||||||
input_devices[ deviceNode->getIndex() ] = inputDevice;
|
input_devices[ deviceNode->getIndex() ] = inputDevice;
|
||||||
}
|
}
|
||||||
catch( ... ) {
|
catch( ... ) {
|
||||||
SG_LOG(SG_INPUT, SG_WARN, "can't open InputDevice " << inputDevice->GetName() );
|
delete inputDevice;
|
||||||
|
SG_LOG(SG_INPUT, SG_ALERT, "can't open InputDevice " << inputDevice->GetName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_INPUT, SG_DEBUG, "using InputDevice " << inputDevice->GetName() );
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,36 @@
|
||||||
#include <simgear/structure/subsystem_mgr.hxx>
|
#include <simgear/structure/subsystem_mgr.hxx>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A base class for event data.
|
* A base structure for event data.
|
||||||
|
* To be extended for O/S specific implementation data
|
||||||
*/
|
*/
|
||||||
struct FGEventData {
|
struct FGEventData {
|
||||||
FGEventData( double aValue, double aDt ) : value(aValue), dt(aDt) {}
|
FGEventData( double aValue, double aDt, int aModifiers ) : value(aValue), dt(aDt), modifiers(aModifiers) {}
|
||||||
|
int modifiers;
|
||||||
double value;
|
double value;
|
||||||
double dt;
|
double dt;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FGEventSetting : public SGReferenced {
|
||||||
|
public:
|
||||||
|
FGEventSetting( SGPropertyNode_ptr base );
|
||||||
|
|
||||||
|
bool Test();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* access for the value property
|
||||||
|
*/
|
||||||
|
double GetValue();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double value;
|
||||||
|
SGPropertyNode_ptr valueNode;
|
||||||
|
SGSharedPtr<const SGCondition> condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SGSharedPtr<FGEventSetting> FGEventSetting_ptr;
|
||||||
|
typedef vector<FGEventSetting_ptr> setting_list_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A wrapper class for a configured event.
|
* A wrapper class for a configured event.
|
||||||
*
|
*
|
||||||
|
@ -51,15 +73,21 @@ struct FGEventData {
|
||||||
* <max type="double">90.0</max>
|
* <max type="double">90.0</max>
|
||||||
* <wrap type="bool">false</wrap>
|
* <wrap type="bool">false</wrap>
|
||||||
* </binding>
|
* </binding>
|
||||||
|
* <mod-xyz>
|
||||||
|
* <binding>
|
||||||
|
* ...
|
||||||
|
* </binding>
|
||||||
|
* </mod-xyz>
|
||||||
* </event>
|
* </event>
|
||||||
*/
|
*/
|
||||||
|
class FGInputDevice;
|
||||||
class FGInputEvent : public SGReferenced,FGCommonInput {
|
class FGInputEvent : public SGReferenced,FGCommonInput {
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
* Constructor for the class. The arg node shall point
|
* Constructor for the class. The arg node shall point
|
||||||
* to the property corresponding to the <event> node
|
* to the property corresponding to the <event> node
|
||||||
*/
|
*/
|
||||||
FGInputEvent( SGPropertyNode_ptr node );
|
FGInputEvent( FGInputDevice * device, SGPropertyNode_ptr node );
|
||||||
virtual ~FGInputEvent();
|
virtual ~FGInputEvent();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -77,7 +105,9 @@ public:
|
||||||
*/
|
*/
|
||||||
string GetDescription() const { return desc; }
|
string GetDescription() const { return desc; }
|
||||||
|
|
||||||
static FGInputEvent * NewObject( SGPropertyNode_ptr node );
|
virtual void update( double dt );
|
||||||
|
|
||||||
|
static FGInputEvent * NewObject( FGInputDevice * device, SGPropertyNode_ptr node );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* A more or less meaningfull description of the event */
|
/* A more or less meaningfull description of the event */
|
||||||
|
@ -89,19 +119,30 @@ protected:
|
||||||
/* A list of SGBinding objects */
|
/* A list of SGBinding objects */
|
||||||
binding_list_t bindings[KEYMOD_MAX];
|
binding_list_t bindings[KEYMOD_MAX];
|
||||||
|
|
||||||
|
/* A list of FGEventSetting objects */
|
||||||
|
setting_list_t settings;
|
||||||
|
|
||||||
|
/* A pointer to the associated device */
|
||||||
|
FGInputDevice * device;
|
||||||
|
|
||||||
double lastDt;
|
double lastDt;
|
||||||
double intervalSec;
|
double intervalSec;
|
||||||
|
double lastSettingValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FGButtonEvent : public FGInputEvent {
|
class FGButtonEvent : public FGInputEvent {
|
||||||
public:
|
public:
|
||||||
FGButtonEvent( SGPropertyNode_ptr node );
|
FGButtonEvent( FGInputDevice * device, SGPropertyNode_ptr node );
|
||||||
virtual void fire( FGEventData & eventData );
|
virtual void fire( FGEventData & eventData );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool repeatable;
|
||||||
|
bool lastState;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FGAxisEvent : public FGInputEvent {
|
class FGAxisEvent : public FGInputEvent {
|
||||||
public:
|
public:
|
||||||
FGAxisEvent( SGPropertyNode_ptr node );
|
FGAxisEvent( FGInputDevice * device, SGPropertyNode_ptr node );
|
||||||
protected:
|
protected:
|
||||||
virtual void fire( FGEventData & eventData );
|
virtual void fire( FGEventData & eventData );
|
||||||
double tolerance;
|
double tolerance;
|
||||||
|
@ -130,6 +171,13 @@ public:
|
||||||
|
|
||||||
virtual void Open() = 0;
|
virtual void Open() = 0;
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
|
||||||
|
virtual void Send( const char * eventName, double value ) = 0;
|
||||||
|
|
||||||
|
inline void Send( const string & eventName, double value ) {
|
||||||
|
Send( eventName.c_str(), value );
|
||||||
|
}
|
||||||
|
|
||||||
virtual const char * TranslateEventName( FGEventData & eventData ) = 0;
|
virtual const char * TranslateEventName( FGEventData & eventData ) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
@ -137,13 +185,19 @@ public:
|
||||||
string & GetName() { return name; }
|
string & GetName() { return name; }
|
||||||
|
|
||||||
void HandleEvent( FGEventData & eventData );
|
void HandleEvent( FGEventData & eventData );
|
||||||
|
|
||||||
void AddHandledEvent( FGInputEvent_ptr handledEvent ) {
|
void AddHandledEvent( FGInputEvent_ptr handledEvent ) {
|
||||||
if( handledEvents.count( handledEvent->GetName() ) == 0 )
|
if( handledEvents.count( handledEvent->GetName() ) == 0 )
|
||||||
handledEvents[handledEvent->GetName()] = handledEvent;
|
handledEvents[handledEvent->GetName()] = handledEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void update( double dt );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// A map of events, this device handles
|
||||||
map<string,FGInputEvent_ptr> handledEvents;
|
map<string,FGInputEvent_ptr> handledEvents;
|
||||||
|
|
||||||
|
// the device has a name to be recognized
|
||||||
string name;
|
string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,6 +213,7 @@ public:
|
||||||
virtual ~FGEventInput();
|
virtual ~FGEventInput();
|
||||||
virtual void init();
|
virtual void init();
|
||||||
virtual void postinit();
|
virtual void postinit();
|
||||||
|
virtual void update( double dt );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const char * PROPERTY_ROOT;
|
static const char * PROPERTY_ROOT;
|
||||||
|
|
437
src/Input/FGLinuxEventInput.cxx
Normal file
437
src/Input/FGLinuxEventInput.cxx
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
// FGEventInput.cxx -- handle event driven input devices for the Linux O/S
|
||||||
|
//
|
||||||
|
// 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 "FGLinuxEventInput.hxx"
|
||||||
|
|
||||||
|
#include <poll.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <dbus/dbus.h>
|
||||||
|
|
||||||
|
struct TypeCode {
|
||||||
|
unsigned type;
|
||||||
|
unsigned code;
|
||||||
|
|
||||||
|
inline unsigned long hashCode() const {
|
||||||
|
return (unsigned long)type << 16 | (unsigned long)code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator < ( const TypeCode other) const {
|
||||||
|
return hashCode() < other.hashCode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// event to name translation table
|
||||||
|
// events are from include <linux/input.h>
|
||||||
|
|
||||||
|
static struct EventTypes {
|
||||||
|
struct TypeCode typeCode;
|
||||||
|
const char * name;
|
||||||
|
} EVENT_TYPES[] = {
|
||||||
|
{ { EV_SYN, SYN_REPORT }, "syn-report" },
|
||||||
|
{ { EV_SYN, SYN_CONFIG }, "syn-config" },
|
||||||
|
|
||||||
|
// misc
|
||||||
|
{ { EV_KEY, BTN_0 }, "button-0" },
|
||||||
|
{ { EV_KEY, BTN_1 }, "button-1" },
|
||||||
|
{ { EV_KEY, BTN_2 }, "button-2" },
|
||||||
|
{ { EV_KEY, BTN_3 }, "button-3" },
|
||||||
|
{ { EV_KEY, BTN_4 }, "button-4" },
|
||||||
|
{ { EV_KEY, BTN_5 }, "button-5" },
|
||||||
|
{ { EV_KEY, BTN_6 }, "button-6" },
|
||||||
|
{ { EV_KEY, BTN_7 }, "button-7" },
|
||||||
|
{ { EV_KEY, BTN_8 }, "button-8" },
|
||||||
|
{ { EV_KEY, BTN_9 }, "button-9" },
|
||||||
|
|
||||||
|
// mouse
|
||||||
|
{ { EV_KEY, BTN_LEFT }, "button-left" },
|
||||||
|
{ { EV_KEY, BTN_RIGHT }, "button-right" },
|
||||||
|
{ { EV_KEY, BTN_MIDDLE }, "button-middle" },
|
||||||
|
{ { EV_KEY, BTN_SIDE }, "button-side" },
|
||||||
|
{ { EV_KEY, BTN_EXTRA }, "button-extra" },
|
||||||
|
{ { EV_KEY, BTN_FORWARD }, "button-forward" },
|
||||||
|
{ { EV_KEY, BTN_BACK }, "button-back" },
|
||||||
|
{ { EV_KEY, BTN_TASK }, "button-task" },
|
||||||
|
|
||||||
|
// joystick
|
||||||
|
{ { EV_KEY, BTN_TRIGGER }, "button-trigger" },
|
||||||
|
{ { EV_KEY, BTN_THUMB }, "button-thumb" },
|
||||||
|
{ { EV_KEY, BTN_THUMB2 }, "button-thumb2" },
|
||||||
|
{ { EV_KEY, BTN_TOP }, "button-top" },
|
||||||
|
{ { EV_KEY, BTN_TOP2 }, "button-top2" },
|
||||||
|
{ { EV_KEY, BTN_PINKIE }, "button-pinkie" },
|
||||||
|
{ { EV_KEY, BTN_BASE }, "button-base" },
|
||||||
|
{ { EV_KEY, BTN_BASE2 }, "button-base2" },
|
||||||
|
{ { EV_KEY, BTN_BASE3 }, "button-base3" },
|
||||||
|
{ { EV_KEY, BTN_BASE4 }, "button-base4" },
|
||||||
|
{ { EV_KEY, BTN_BASE5 }, "button-base5" },
|
||||||
|
{ { EV_KEY, BTN_BASE6 }, "button-base6" },
|
||||||
|
{ { EV_KEY, BTN_DEAD }, "button-dead" },
|
||||||
|
|
||||||
|
// gamepad
|
||||||
|
{ { EV_KEY, BTN_A }, "button-a" },
|
||||||
|
{ { EV_KEY, BTN_B }, "button-b" },
|
||||||
|
{ { EV_KEY, BTN_C }, "button-c" },
|
||||||
|
{ { EV_KEY, BTN_X }, "button-x" },
|
||||||
|
{ { EV_KEY, BTN_Y }, "button-y" },
|
||||||
|
{ { EV_KEY, BTN_Z }, "button-z" },
|
||||||
|
{ { EV_KEY, BTN_TL }, "button-tl" },
|
||||||
|
{ { EV_KEY, BTN_TR }, "button-tr" },
|
||||||
|
{ { EV_KEY, BTN_TL2 }, "button-tl2" },
|
||||||
|
{ { EV_KEY, BTN_TR2 }, "button-tr2" },
|
||||||
|
{ { EV_KEY, BTN_SELECT }, "button-select" },
|
||||||
|
{ { EV_KEY, BTN_START }, "button-start" },
|
||||||
|
{ { EV_KEY, BTN_MODE }, "button-mode" },
|
||||||
|
{ { EV_KEY, BTN_THUMBL }, "button-thumbl" },
|
||||||
|
{ { EV_KEY, BTN_THUMBR }, "button-thumbr" },
|
||||||
|
|
||||||
|
// digitizer
|
||||||
|
{ { EV_KEY, BTN_TOOL_PEN }, "button-pen" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_RUBBER }, "button-rubber" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_BRUSH }, "button-brush" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_PENCIL }, "button-pencil" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_AIRBRUSH }, "button-airbrush" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_FINGER }, "button-finger" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_MOUSE }, "button-mouse" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_LENS }, "button-lens" },
|
||||||
|
{ { EV_KEY, BTN_TOUCH }, "button-touch" },
|
||||||
|
{ { EV_KEY, BTN_STYLUS }, "button-stylus" },
|
||||||
|
{ { EV_KEY, BTN_STYLUS2 }, "button-stylus2" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_DOUBLETAP }, "button-doubletap" },
|
||||||
|
{ { EV_KEY, BTN_TOOL_TRIPLETAP }, "button-trippletap" },
|
||||||
|
|
||||||
|
{ { EV_KEY, BTN_WHEEL }, "button-wheel" },
|
||||||
|
{ { EV_KEY, BTN_GEAR_DOWN }, "button-gear-down" },
|
||||||
|
{ { EV_KEY, BTN_GEAR_UP }, "button-gear-up" },
|
||||||
|
|
||||||
|
{ { EV_REL, REL_X }, "rel-x-translate" },
|
||||||
|
{ { EV_REL, REL_Y}, "rel-y-translate" },
|
||||||
|
{ { EV_REL, REL_Z}, "rel-z-translate" },
|
||||||
|
{ { EV_REL, REL_RX}, "rel-x-rotate" },
|
||||||
|
{ { EV_REL, REL_RY}, "rel-y-rotate" },
|
||||||
|
{ { EV_REL, REL_RZ}, "rel-z-rotate" },
|
||||||
|
{ { EV_REL, REL_HWHEEL}, "rel-hwheel" },
|
||||||
|
{ { EV_REL, REL_DIAL}, "rel-dial" },
|
||||||
|
{ { EV_REL, REL_WHEEL}, "rel-wheel" },
|
||||||
|
{ { EV_REL, REL_MISC}, "rel-misc" },
|
||||||
|
|
||||||
|
{ { EV_ABS, ABS_X }, "abs-x-translate" },
|
||||||
|
{ { EV_ABS, ABS_Y }, "abs-y-translate" },
|
||||||
|
{ { EV_ABS, ABS_Z }, "abs-z-translate" },
|
||||||
|
{ { EV_ABS, ABS_RX }, "abs-x-rotate" },
|
||||||
|
{ { EV_ABS, ABS_RY }, "abs-y-rotate" },
|
||||||
|
{ { EV_ABS, ABS_RZ }, "abs-z-rotate" },
|
||||||
|
{ { EV_ABS, ABS_THROTTLE }, "abs-throttle" },
|
||||||
|
{ { EV_ABS, ABS_RUDDER }, "abs-rudder" },
|
||||||
|
{ { EV_ABS, ABS_WHEEL }, "abs-wheel" },
|
||||||
|
{ { EV_ABS, ABS_GAS }, "abs-gas" },
|
||||||
|
{ { EV_ABS, ABS_BRAKE }, "abs-brake" },
|
||||||
|
{ { EV_ABS, ABS_HAT0X }, "abs-hat0-x" },
|
||||||
|
{ { EV_ABS, ABS_HAT0Y }, "abs-hat0-y" },
|
||||||
|
{ { EV_ABS, ABS_HAT1X }, "abs-hat1-x" },
|
||||||
|
{ { EV_ABS, ABS_HAT1Y }, "abs-hat1-y" },
|
||||||
|
{ { EV_ABS, ABS_HAT2X }, "abs-hat2-x" },
|
||||||
|
{ { EV_ABS, ABS_HAT2Y }, "abs-hat2-y" },
|
||||||
|
{ { EV_ABS, ABS_HAT3X }, "abs-hat3-x" },
|
||||||
|
{ { EV_ABS, ABS_HAT3Y }, "abs-hat3-y" },
|
||||||
|
{ { EV_ABS, ABS_PRESSURE }, "abs-pressure" },
|
||||||
|
{ { EV_ABS, ABS_DISTANCE }, "abs-distance" },
|
||||||
|
{ { EV_ABS, ABS_TILT_X }, "abs-tilt-x" },
|
||||||
|
{ { EV_ABS, ABS_TILT_Y }, "abs-tilt-y" },
|
||||||
|
{ { EV_ABS, ABS_TOOL_WIDTH }, "abs-toold-width" },
|
||||||
|
{ { EV_ABS, ABS_VOLUME }, "abs-volume" },
|
||||||
|
{ { EV_ABS, ABS_MISC }, "abs-misc" },
|
||||||
|
|
||||||
|
{ { EV_MSC, MSC_SERIAL }, "misc-serial" },
|
||||||
|
{ { EV_MSC, MSC_PULSELED }, "misc-pulseled" },
|
||||||
|
{ { EV_MSC, MSC_GESTURE }, "misc-gesture" },
|
||||||
|
{ { EV_MSC, MSC_RAW }, "misc-raw" },
|
||||||
|
{ { EV_MSC, MSC_SCAN }, "misc-scan" },
|
||||||
|
|
||||||
|
// switch
|
||||||
|
{ { EV_SW, SW_LID }, "switch-lid" },
|
||||||
|
{ { EV_SW, SW_TABLET_MODE }, "switch-tablet-mode" },
|
||||||
|
{ { EV_SW, SW_HEADPHONE_INSERT }, "switch-headphone-insert" },
|
||||||
|
{ { EV_SW, SW_RFKILL_ALL }, "switch-rfkill" },
|
||||||
|
{ { EV_SW, SW_MICROPHONE_INSERT }, "switch-microphone-insert" },
|
||||||
|
{ { EV_SW, SW_DOCK }, "swtich-dock" },
|
||||||
|
|
||||||
|
{ { EV_LED, LED_NUML}, "led-numlock" },
|
||||||
|
{ { EV_LED, LED_CAPSL}, "led-capslock" },
|
||||||
|
{ { EV_LED, LED_SCROLLL}, "led-scrolllock" },
|
||||||
|
{ { EV_LED, LED_COMPOSE}, "led-compose" },
|
||||||
|
{ { EV_LED, LED_KANA}, "led-kana" },
|
||||||
|
{ { EV_LED, LED_SLEEP}, "led-sleep" },
|
||||||
|
{ { EV_LED, LED_SUSPEND}, "led-suspend" },
|
||||||
|
{ { EV_LED, LED_MUTE}, "led-mute" },
|
||||||
|
{ { EV_LED, LED_MISC}, "led-misc" },
|
||||||
|
{ { EV_LED, LED_MAIL}, "led-mail" },
|
||||||
|
{ { EV_LED, LED_CHARGING}, "led-charging" }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class EventNameByType : public map<TypeCode,const char*> {
|
||||||
|
public:
|
||||||
|
EventNameByType() {
|
||||||
|
for( int i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
|
||||||
|
(*this)[EVENT_TYPES[i].typeCode] = EVENT_TYPES[i].name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static EventNameByType EVENT_NAME_BY_TYPE;
|
||||||
|
|
||||||
|
|
||||||
|
struct ltstr {
|
||||||
|
bool operator()(const char * s1, const char * s2 ) const {
|
||||||
|
return strcmp( s1, s2 ) < 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EventTypeByName : public map<const char *,TypeCode,ltstr> {
|
||||||
|
public:
|
||||||
|
EventTypeByName() {
|
||||||
|
for( int i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
|
||||||
|
(*this)[EVENT_TYPES[i].name] = EVENT_TYPES[i].typeCode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static EventTypeByName EVENT_TYPE_BY_NAME;
|
||||||
|
|
||||||
|
|
||||||
|
FGLinuxInputDevice::FGLinuxInputDevice( string aName, string aDevname ) :
|
||||||
|
FGInputDevice(aName),
|
||||||
|
fd(-1),
|
||||||
|
devname( aDevname )
|
||||||
|
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGLinuxInputDevice::~FGLinuxInputDevice()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
catch(...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FGLinuxInputDevice::FGLinuxInputDevice() :
|
||||||
|
fd(-1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLinuxInputDevice::Open()
|
||||||
|
{
|
||||||
|
if( fd != -1 ) return;
|
||||||
|
if( (fd = ::open( devname.c_str(), O_RDWR )) == -1 ) {
|
||||||
|
throw exception();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
input_event evt;
|
||||||
|
evt.type=EV_LED;
|
||||||
|
evt.code = 8;
|
||||||
|
evt.value = 1;
|
||||||
|
evt.time.tv_sec = 0;
|
||||||
|
evt.time.tv_usec = 0;
|
||||||
|
write( fd, &evt, sizeof(evt) );
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLinuxInputDevice::Close()
|
||||||
|
{
|
||||||
|
if( fd != -1 ) ::close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLinuxInputDevice::Send( const char * eventName, double value )
|
||||||
|
{
|
||||||
|
if( EVENT_TYPE_BY_NAME.count( eventName ) <= 0 ) {
|
||||||
|
SG_LOG( SG_INPUT, SG_ALERT, "Can't send unknown event " << eventName );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeCode & typeCode = EVENT_TYPE_BY_NAME[ eventName ];
|
||||||
|
|
||||||
|
if( fd == -1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_event evt;
|
||||||
|
evt.type=typeCode.type;
|
||||||
|
evt.code = typeCode.code;
|
||||||
|
evt.value = (long)value;
|
||||||
|
evt.time.tv_sec = 0;
|
||||||
|
evt.time.tv_usec = 0;
|
||||||
|
write( fd, &evt, sizeof(evt) );
|
||||||
|
SG_LOG( SG_INPUT, SG_DEBUG, "Written event " << eventName
|
||||||
|
<< " as type=" << evt.type << ", code=" << evt.code << " value=" << evt.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
static char ugly_buffer[128];
|
||||||
|
const char * FGLinuxInputDevice::TranslateEventName( FGEventData & eventData )
|
||||||
|
{
|
||||||
|
FGLinuxEventData & linuxEventData = (FGLinuxEventData&)eventData;
|
||||||
|
TypeCode typeCode;
|
||||||
|
typeCode.type = linuxEventData.type;
|
||||||
|
typeCode.code = linuxEventData.code;
|
||||||
|
if( EVENT_NAME_BY_TYPE.count(typeCode) == 0 ) {
|
||||||
|
sprintf( ugly_buffer, "unknown-%u-%u", (unsigned)linuxEventData.type, (unsigned)linuxEventData.code );
|
||||||
|
return ugly_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EVENT_NAME_BY_TYPE[typeCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLinuxInputDevice::SetDevname( string name )
|
||||||
|
{
|
||||||
|
this->devname = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
FGLinuxEventInput::FGLinuxEventInput() :
|
||||||
|
halcontext(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FGLinuxEventInput::~FGLinuxEventInput()
|
||||||
|
{
|
||||||
|
if( halcontext != NULL ) {
|
||||||
|
libhal_ctx_shutdown( halcontext, NULL);
|
||||||
|
libhal_ctx_free( halcontext );
|
||||||
|
halcontext = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//TODO: enable hotplug support
|
||||||
|
static void DeviceAddedCallback (LibHalContext *ctx, const char *udi)
|
||||||
|
{
|
||||||
|
FGLinuxEventInput * linuxEventInput = (FGLinuxEventInput*)libhal_ctx_get_user_data (ctx);
|
||||||
|
linuxEventInput->AddHalDevice( udi );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DeviceRemovedCallback (LibHalContext *ctx, const char *udi)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void FGLinuxEventInput::init()
|
||||||
|
{
|
||||||
|
FGEventInput::init();
|
||||||
|
|
||||||
|
DBusConnection * connection;
|
||||||
|
DBusError dbus_error;
|
||||||
|
|
||||||
|
dbus_error_init(&dbus_error);
|
||||||
|
connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
|
||||||
|
if (dbus_error_is_set(&dbus_error)) {
|
||||||
|
SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to system bus " << dbus_error.message);
|
||||||
|
dbus_error_free (&dbus_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
halcontext = libhal_ctx_new();
|
||||||
|
|
||||||
|
libhal_ctx_set_dbus_connection (halcontext, connection );
|
||||||
|
dbus_error_init (&dbus_error);
|
||||||
|
|
||||||
|
if( libhal_ctx_init( halcontext, &dbus_error )) {
|
||||||
|
|
||||||
|
int num_devices = 0;
|
||||||
|
char ** devices = libhal_find_device_by_capability(halcontext, "input", &num_devices, NULL);
|
||||||
|
|
||||||
|
for ( int i = 0; i < num_devices; i++)
|
||||||
|
AddHalDevice( devices[i] );
|
||||||
|
|
||||||
|
libhal_free_string_array (devices);
|
||||||
|
|
||||||
|
//TODO: enable hotplug support
|
||||||
|
// libhal_ctx_set_user_data( halcontext, this );
|
||||||
|
// libhal_ctx_set_device_added( halcontext, DeviceAddedCallback );
|
||||||
|
// libhal_ctx_set_device_removed( halcontext, DeviceRemovedCallback );
|
||||||
|
} else {
|
||||||
|
if(dbus_error_is_set (&dbus_error) ) {
|
||||||
|
SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to hald: " << dbus_error.message);
|
||||||
|
dbus_error_free (&dbus_error);
|
||||||
|
} else {
|
||||||
|
SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to hald." );
|
||||||
|
}
|
||||||
|
|
||||||
|
libhal_ctx_free (halcontext);
|
||||||
|
halcontext = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLinuxEventInput::AddHalDevice( const char * udi )
|
||||||
|
{
|
||||||
|
char * device = libhal_device_get_property_string( halcontext, udi, "input.device", NULL);
|
||||||
|
char * product = libhal_device_get_property_string( halcontext, udi, "input.product", NULL);
|
||||||
|
|
||||||
|
if( product != NULL && device != NULL )
|
||||||
|
AddDevice( new FGLinuxInputDevice(product, device) );
|
||||||
|
else
|
||||||
|
SG_LOG( SG_INPUT, SG_ALERT, "Can't get device or product property of " << udi );
|
||||||
|
|
||||||
|
if( device != NULL ) libhal_free_string( device );
|
||||||
|
if( product != NULL ) libhal_free_string( product );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FGLinuxEventInput::update( double dt )
|
||||||
|
{
|
||||||
|
FGEventInput::update( dt );
|
||||||
|
|
||||||
|
// index the input devices by the associated fd and prepare
|
||||||
|
// the pollfd array by filling in the file descriptor
|
||||||
|
struct pollfd fds[input_devices.size()];
|
||||||
|
map<int,FGLinuxInputDevice*> devicesByFd;
|
||||||
|
map<int,FGInputDevice*>::const_iterator it;
|
||||||
|
int i;
|
||||||
|
for( i=0, it = input_devices.begin(); it != input_devices.end(); ++it, i++ ) {
|
||||||
|
FGInputDevice* p = (*it).second;
|
||||||
|
int fd = ((FGLinuxInputDevice*)p)->GetFd();
|
||||||
|
fds[i].fd = fd;
|
||||||
|
fds[i].events = POLLIN;
|
||||||
|
devicesByFd[fd] = (FGLinuxInputDevice*)p;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modifiers = fgGetKeyModifiers();
|
||||||
|
// poll all devices until no more events are in the queue
|
||||||
|
// do no more than maxpolls in a single loop to prevent locking
|
||||||
|
int maxpolls = 100;
|
||||||
|
while( maxpolls-- > 0 && ::poll( fds, i, 0 ) > 0 ) {
|
||||||
|
for( int i = 0; i < sizeof(fds)/sizeof(fds[0]); i++ ) {
|
||||||
|
if( fds[i].revents & POLLIN ) {
|
||||||
|
|
||||||
|
// if this device reports data ready, it should be a input_event struct
|
||||||
|
struct input_event event;
|
||||||
|
|
||||||
|
if( read( fds[i].fd, &event, sizeof(event) ) != sizeof(event) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
FGLinuxEventData eventData( event, dt, modifiers );
|
||||||
|
|
||||||
|
// let the FGInputDevice handle the data
|
||||||
|
devicesByFd[fds[i].fd]->HandleEvent( eventData );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
src/Input/FGLinuxEventInput.hxx
Normal file
76
src/Input/FGLinuxEventInput.hxx
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// FGEventInput.hxx -- handle event driven input devices for the Linux O/S
|
||||||
|
//
|
||||||
|
// 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 __FGLINUXEVENTINPUT_HXX
|
||||||
|
#define __FGLINUXEVENTINPUT_HXX
|
||||||
|
|
||||||
|
#include "FGEventInput.hxx"
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <hal/libhal.h>
|
||||||
|
|
||||||
|
struct FGLinuxEventData : public FGEventData {
|
||||||
|
FGLinuxEventData( struct input_event & event, double dt, int modifiers ) :
|
||||||
|
FGEventData( (double)event.value, dt, modifiers ),
|
||||||
|
type(event.type),
|
||||||
|
code(event.code) {
|
||||||
|
}
|
||||||
|
unsigned type;
|
||||||
|
unsigned code;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A implementation for linux event devices
|
||||||
|
*/
|
||||||
|
class FGLinuxInputDevice : public FGInputDevice {
|
||||||
|
public:
|
||||||
|
FGLinuxInputDevice();
|
||||||
|
FGLinuxInputDevice( string name, string devname );
|
||||||
|
virtual ~FGLinuxInputDevice();
|
||||||
|
|
||||||
|
virtual void Open();
|
||||||
|
virtual void Close();
|
||||||
|
virtual void Send( const char * eventName, double value );
|
||||||
|
virtual const char * TranslateEventName( FGEventData & eventData );
|
||||||
|
|
||||||
|
void SetDevname( const string name );
|
||||||
|
string GetDevname() const { return devname; }
|
||||||
|
|
||||||
|
int GetFd() { return fd; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
string devname;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FGLinuxEventInput : public FGEventInput {
|
||||||
|
public:
|
||||||
|
FGLinuxEventInput();
|
||||||
|
virtual ~ FGLinuxEventInput();
|
||||||
|
virtual void update (double dt);
|
||||||
|
virtual void init();
|
||||||
|
|
||||||
|
void AddHalDevice( const char * udi );
|
||||||
|
protected:
|
||||||
|
LibHalContext *halcontext;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,12 +2,21 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkgdatadir)\"
|
||||||
|
|
||||||
noinst_LIBRARIES = libInput.a
|
noinst_LIBRARIES = libInput.a
|
||||||
|
|
||||||
|
if WITH_EVENTINPUT
|
||||||
|
libInput_Event_SOURCES = FGEventInput.cxx FGEventInput.hxx FGLinuxEventInput.cxx FGLinuxEventInput.hxx
|
||||||
|
libInput_Event_INCLUDES = -I/usr/include/dbus-1.0 -I/usr/lib64/dbus-1.0/include
|
||||||
|
else
|
||||||
|
libInput_Event_SOURCES =
|
||||||
|
libInput_Event_INCLUDES =
|
||||||
|
endif
|
||||||
|
|
||||||
libInput_a_SOURCES = input.cxx input.hxx FGCommonInput.cxx FGCommonInput.hxx \
|
libInput_a_SOURCES = input.cxx input.hxx FGCommonInput.cxx FGCommonInput.hxx \
|
||||||
FGDeviceConfigurationMap.cxx FGDeviceConfigurationMap.hxx \
|
FGDeviceConfigurationMap.cxx FGDeviceConfigurationMap.hxx \
|
||||||
FGButton.cxx FGButton.hxx \
|
FGButton.cxx FGButton.hxx \
|
||||||
FGMouseInput.cxx FGMouseInput.hxx \
|
FGMouseInput.cxx FGMouseInput.hxx \
|
||||||
FGKeyboardInput.cxx FGKeyboardInput.hxx \
|
FGKeyboardInput.cxx FGKeyboardInput.hxx \
|
||||||
FGJoystickInput.cxx FGJoystickInput.hxx
|
FGJoystickInput.cxx FGJoystickInput.hxx \
|
||||||
|
$(libInput_Event_SOURCES)
|
||||||
|
|
||||||
bin_PROGRAMS = js_demo fgjs
|
bin_PROGRAMS = js_demo fgjs
|
||||||
|
|
||||||
|
@ -20,4 +29,5 @@ fgjs_SOURCES = fgjs.cxx jsinput.cxx jsinput.h jssuper.cxx jssuper.h
|
||||||
fgjs_LDADD = -lplibjs -lplibul $(base_LIBS) $(joystick_LIBS) \
|
fgjs_LDADD = -lplibjs -lplibul $(base_LIBS) $(joystick_LIBS) \
|
||||||
-lsgprops -lsgmisc -lsgio -lsgdebug -lsgstructure -lsgxml -lz
|
-lsgprops -lsgmisc -lsgio -lsgdebug -lsgstructure -lsgxml -lz
|
||||||
|
|
||||||
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/src/Main
|
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/src/Main \
|
||||||
|
$(libInput_Event_INCLUDES)
|
||||||
|
|
|
@ -32,7 +32,20 @@
|
||||||
#include "FGJoystickInput.hxx"
|
#include "FGJoystickInput.hxx"
|
||||||
|
|
||||||
#ifdef WITH_EVENTINPUT
|
#ifdef WITH_EVENTINPUT
|
||||||
|
#if defined( UL_WIN32 )
|
||||||
|
//to be developed
|
||||||
|
//#include "FGDirectXEventInput.hxx"
|
||||||
|
//#define INPUTEVENT_CLASS FGDirectXEventInput
|
||||||
|
#elif defined ( UL_MAC_OSX )
|
||||||
|
/*
|
||||||
|
Currently not supported :-(
|
||||||
|
*/
|
||||||
|
#undef INPUTEVENT_CLASS
|
||||||
|
#else
|
||||||
#include "FGLinuxEventInput.hxx"
|
#include "FGLinuxEventInput.hxx"
|
||||||
|
#define INPUTEVENT_CLASS FGLinuxEventInput
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -45,6 +58,9 @@ FGInput::FGInput ()
|
||||||
set_subsystem( "input-mouse", new FGMouseInput() );
|
set_subsystem( "input-mouse", new FGMouseInput() );
|
||||||
set_subsystem( "input-keyboard", new FGKeyboardInput() );
|
set_subsystem( "input-keyboard", new FGKeyboardInput() );
|
||||||
set_subsystem( "input-joystick", new FGJoystickInput() );
|
set_subsystem( "input-joystick", new FGJoystickInput() );
|
||||||
|
#ifdef INPUTEVENT_CLASS
|
||||||
|
set_subsystem( "input-event", new INPUTEVENT_CLASS() );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FGInput::~FGInput ()
|
FGInput::~FGInput ()
|
||||||
|
|
|
@ -15,6 +15,12 @@ else
|
||||||
THREAD_LIBS =
|
THREAD_LIBS =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if WITH_EVENTINPUT
|
||||||
|
EVENT_LIBS = -ldbus-1 -lhal
|
||||||
|
else
|
||||||
|
EVENT_LIBS =
|
||||||
|
endif
|
||||||
|
|
||||||
GFX_CODE = fg_os_osgviewer.cxx fg_os_common.cxx fg_os.hxx
|
GFX_CODE = fg_os_osgviewer.cxx fg_os_common.cxx fg_os.hxx
|
||||||
|
|
||||||
JSBSIM_LIBS = \
|
JSBSIM_LIBS = \
|
||||||
|
@ -105,7 +111,8 @@ fgfs_LDADD = \
|
||||||
$(network_LIBS) \
|
$(network_LIBS) \
|
||||||
-lz \
|
-lz \
|
||||||
$(opengl_LIBS) \
|
$(opengl_LIBS) \
|
||||||
$(openal_LIBS)
|
$(openal_LIBS) \
|
||||||
|
$(EVENT_LIBS)
|
||||||
|
|
||||||
metar_SOURCES = metar_main.cxx
|
metar_SOURCES = metar_main.cxx
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue