1
0
Fork 0

Advanced input subsystem - Step3: Adding support for the Linux event devices

This commit is contained in:
torsten 2009-08-11 15:59:30 +00:00 committed by Tim Moore
parent 3bce312213
commit 5c57153e62
8 changed files with 738 additions and 36 deletions

View file

@ -112,6 +112,14 @@ fi
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
# defaults to yes
AC_ARG_WITH(threads, [ --with-threads Include tile loading threads [default=yes]], [], [with_threads=yes])
@ -760,6 +768,12 @@ else
echo "threads: no"
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
echo "Include special purpose flight models: yes"
else

View file

@ -20,60 +20,117 @@
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "FGEventInput.hxx"
#include <Main/fg_props.hxx>
#include <simgear/io/sg_file.hxx>
#include <poll.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 )
{
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-" ) )
return new FGButtonEvent( node );
return new FGButtonEvent( device, node );
if( StartsWith( name, "rel-" ) )
return new FGAxisEvent( node );
return new FGAxisEvent( device, node );
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 ) :
lastDt(0.0)
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" );
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 );
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()
{
}
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[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 );
lastDt -= intervalSec;
}
}
FGAxisEvent::FGAxisEvent( SGPropertyNode_ptr node ) :
FGInputEvent( node )
FGAxisEvent::FGAxisEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
FGInputEvent( device, node )
{
tolerance = node->getDoubleValue("tolerance", 0.002);
minRange = node->getDoubleValue("min-range", -1024.0);
@ -93,24 +150,49 @@ void FGAxisEvent::fire( FGEventData & eventData )
FGInputEvent::fire( eventData );
}
FGButtonEvent::FGButtonEvent( SGPropertyNode_ptr node ) :
FGInputEvent( node )
FGButtonEvent::FGButtonEvent( FGInputDevice * device, SGPropertyNode_ptr node ) :
FGInputEvent( device, node ),
lastState(false),
repeatable(false)
{
repeatable = node->getBoolValue("repeatable", repeatable);
}
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()
{
}
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 );
cout << GetName() << " has event " << eventName << endl;
// cout << GetName() << " has event " << eventName << " modifiers=" << eventData.modifiers << " value=" << eventData.value << endl;
if( handledEvents.count( eventName ) > 0 ) {
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 )
{
SGPropertyNode_ptr baseNode = fgGetNode( PROPERTY_ROOT, true );
@ -174,19 +263,14 @@ void FGEventInput::AddDevice( FGInputDevice * inputDevice )
}
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;
}
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 );
}
for( vector<SGPropertyNode_ptr>::iterator it = eventNodes.begin(); it != eventNodes.end(); it++ )
inputDevice->AddHandledEvent( FGInputEvent::NewObject( inputDevice, *it ) );
// TODO:
// add nodes for the last event:
@ -198,6 +282,9 @@ void FGEventInput::AddDevice( FGInputDevice * inputDevice )
input_devices[ deviceNode->getIndex() ] = inputDevice;
}
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() );
}

View file

@ -29,14 +29,36 @@
#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 {
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 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.
*
@ -51,15 +73,21 @@ struct FGEventData {
* <max type="double">90.0</max>
* <wrap type="bool">false</wrap>
* </binding>
* <mod-xyz>
* <binding>
* ...
* </binding>
* </mod-xyz>
* </event>
*/
class FGInputDevice;
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 );
FGInputEvent( FGInputDevice * device, SGPropertyNode_ptr node );
virtual ~FGInputEvent();
/*
@ -77,7 +105,9 @@ public:
*/
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:
/* A more or less meaningfull description of the event */
@ -89,19 +119,30 @@ protected:
/* A list of SGBinding objects */
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 intervalSec;
double lastSettingValue;
};
class FGButtonEvent : public FGInputEvent {
public:
FGButtonEvent( SGPropertyNode_ptr node );
FGButtonEvent( FGInputDevice * device, SGPropertyNode_ptr node );
virtual void fire( FGEventData & eventData );
protected:
bool repeatable;
bool lastState;
};
class FGAxisEvent : public FGInputEvent {
public:
FGAxisEvent( SGPropertyNode_ptr node );
FGAxisEvent( FGInputDevice * device, SGPropertyNode_ptr node );
protected:
virtual void fire( FGEventData & eventData );
double tolerance;
@ -130,6 +171,13 @@ public:
virtual void Open() = 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;
@ -137,13 +185,19 @@ public:
string & GetName() { return name; }
void HandleEvent( FGEventData & eventData );
void AddHandledEvent( FGInputEvent_ptr handledEvent ) {
if( handledEvents.count( handledEvent->GetName() ) == 0 )
handledEvents[handledEvent->GetName()] = handledEvent;
}
virtual void update( double dt );
private:
// A map of events, this device handles
map<string,FGInputEvent_ptr> handledEvents;
// the device has a name to be recognized
string name;
};
@ -159,6 +213,7 @@ public:
virtual ~FGEventInput();
virtual void init();
virtual void postinit();
virtual void update( double dt );
protected:
static const char * PROPERTY_ROOT;

View 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 );
}
}
}
}

View 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

View file

@ -2,12 +2,21 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkgdatadir)\"
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 \
FGDeviceConfigurationMap.cxx FGDeviceConfigurationMap.hxx \
FGButton.cxx FGButton.hxx \
FGMouseInput.cxx FGMouseInput.hxx \
FGKeyboardInput.cxx FGKeyboardInput.hxx \
FGJoystickInput.cxx FGJoystickInput.hxx
FGJoystickInput.cxx FGJoystickInput.hxx \
$(libInput_Event_SOURCES)
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) \
-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)

View file

@ -32,7 +32,20 @@
#include "FGJoystickInput.hxx"
#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"
#define INPUTEVENT_CLASS FGLinuxEventInput
#endif
#endif
////////////////////////////////////////////////////////////////////////
@ -45,6 +58,9 @@ FGInput::FGInput ()
set_subsystem( "input-mouse", new FGMouseInput() );
set_subsystem( "input-keyboard", new FGKeyboardInput() );
set_subsystem( "input-joystick", new FGJoystickInput() );
#ifdef INPUTEVENT_CLASS
set_subsystem( "input-event", new INPUTEVENT_CLASS() );
#endif
}
FGInput::~FGInput ()

View file

@ -15,6 +15,12 @@ else
THREAD_LIBS =
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
JSBSIM_LIBS = \
@ -105,7 +111,8 @@ fgfs_LDADD = \
$(network_LIBS) \
-lz \
$(opengl_LIBS) \
$(openal_LIBS)
$(openal_LIBS) \
$(EVENT_LIBS)
metar_SOURCES = metar_main.cxx