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")
|
||||
|
||||
|
||||
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
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
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() );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
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
|
||||
|
||||
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)
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue