From 5c57153e6233a112fa3aec8fd648a1a7d8743099 Mon Sep 17 00:00:00 2001 From: torsten Date: Tue, 11 Aug 2009 15:59:30 +0000 Subject: [PATCH] Advanced input subsystem - Step3: Adding support for the Linux event devices --- configure.ac | 14 + src/Input/FGEventInput.cxx | 141 +++++++++-- src/Input/FGEventInput.hxx | 67 ++++- src/Input/FGLinuxEventInput.cxx | 437 ++++++++++++++++++++++++++++++++ src/Input/FGLinuxEventInput.hxx | 76 ++++++ src/Input/Makefile.am | 14 +- src/Input/input.cxx | 16 ++ src/Main/Makefile.am | 9 +- 8 files changed, 738 insertions(+), 36 deletions(-) create mode 100644 src/Input/FGLinuxEventInput.cxx create mode 100644 src/Input/FGLinuxEventInput.hxx diff --git a/configure.ac b/configure.ac index 1af45ab9d..38ef030f1 100644 --- a/configure.ac +++ b/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 diff --git a/src/Input/FGEventInput.cxx b/src/Input/FGEventInput.cxx index bf0413671..f47a6161a 100644 --- a/src/Input/FGEventInput.cxx +++ b/src/Input/FGEventInput.cxx @@ -20,60 +20,117 @@ // // $Id$ +#ifdef HAVE_CONFIG_H +# include +#endif + #include "FGEventInput.hxx" #include
#include #include #include +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 nor 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::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 settingNodes = node->getChildren("setting"); + for( vector::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::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::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 eventNodes = deviceNode->getChildren( "event" ); - for( vector::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::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() ); } diff --git a/src/Input/FGEventInput.hxx b/src/Input/FGEventInput.hxx index 8b4e0d941..7a7323d43 100644 --- a/src/Input/FGEventInput.hxx +++ b/src/Input/FGEventInput.hxx @@ -29,14 +29,36 @@ #include /* - * 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 condition; +}; + +typedef SGSharedPtr FGEventSetting_ptr; +typedef vector setting_list_t; + /* * A wrapper class for a configured event. * @@ -51,15 +73,21 @@ struct FGEventData { * 90.0 * false * + * + * + * ... + * + * * */ +class FGInputDevice; class FGInputEvent : public SGReferenced,FGCommonInput { public: /* * Constructor for the class. The arg node shall point * to the property corresponding to the 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 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; diff --git a/src/Input/FGLinuxEventInput.cxx b/src/Input/FGLinuxEventInput.cxx new file mode 100644 index 000000000..b0de0d617 --- /dev/null +++ b/src/Input/FGLinuxEventInput.cxx @@ -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 +#endif + +#include "FGLinuxEventInput.hxx" + +#include +#include +#include + +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 + +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 { +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 { +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 devicesByFd; + map::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 ); + } + } + } +} diff --git a/src/Input/FGLinuxEventInput.hxx b/src/Input/FGLinuxEventInput.hxx new file mode 100644 index 000000000..976d1642a --- /dev/null +++ b/src/Input/FGLinuxEventInput.hxx @@ -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 +#include + +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 diff --git a/src/Input/Makefile.am b/src/Input/Makefile.am index d51d2887c..c9d9a238d 100644 --- a/src/Input/Makefile.am +++ b/src/Input/Makefile.am @@ -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) diff --git a/src/Input/input.cxx b/src/Input/input.cxx index 6a36f9d80..40964c285 100644 --- a/src/Input/input.cxx +++ b/src/Input/input.cxx @@ -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 () diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index 041e69033..e30701f7b 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -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 -- 2.39.5