]> git.mxchange.org Git - flightgear.git/commitdiff
Advanced input subsystem - Step3: Adding support for the Linux event devices
authortorsten <torsten>
Tue, 11 Aug 2009 15:59:30 +0000 (15:59 +0000)
committerTim Moore <timoore@redhat.com>
Wed, 12 Aug 2009 21:45:54 +0000 (23:45 +0200)
configure.ac
src/Input/FGEventInput.cxx
src/Input/FGEventInput.hxx
src/Input/FGLinuxEventInput.cxx [new file with mode: 0644]
src/Input/FGLinuxEventInput.hxx [new file with mode: 0644]
src/Input/Makefile.am
src/Input/input.cxx
src/Main/Makefile.am

index 1af45ab9dfaaf21fb7e4d0f00563af965320aa6f..38ef030f1a07afcde1df858f7189d032071cdf7b 100644 (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
index bf041367113ce8302a60e2e4f00a8cf83b4e9c2d..f47a6161aeae737a35edbbb768c8d88e58d34c25 100644 (file)
 //
 // $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()  );
 }
index 8b4e0d941359eebf850d46c9bb731f7ae76546f7..7a7323d4370dd5a5903a0344a5abb1c7348b20fd 100644 (file)
 #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;
diff --git a/src/Input/FGLinuxEventInput.cxx b/src/Input/FGLinuxEventInput.cxx
new file mode 100644 (file)
index 0000000..b0de0d6
--- /dev/null
@@ -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 );
+      }
+    }
+  }
+}
diff --git a/src/Input/FGLinuxEventInput.hxx b/src/Input/FGLinuxEventInput.hxx
new file mode 100644 (file)
index 0000000..976d164
--- /dev/null
@@ -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
index d51d2887c03d0c7a53e43d5998412454ab605b60..c9d9a238d5533a12908cd18dbe5884dd395c432e 100644 (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)
index 6a36f9d806f0d87cb75d7a4fe661a60cc300cac7..40964c28557d022fa48faafa4626ea50375f9fda 100644 (file)
 #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 ()
index 041e69033727e2d6cd6b7d2b1a9e7b806f4c417d..e30701f7ba0f1578d23aafc937de1c44a8ea360d 100644 (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