//
// $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);
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 );
}
{
}
+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 );
}
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:
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() );
}
--- /dev/null
+// 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 );
+ }
+ }
+ }
+}