// 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 <cstring>
+#include <sys/types.h>
+#include <sys/stat.h>
#include "FGLinuxEventInput.hxx"
+#include <libudev.h>
#include <poll.h>
#include <linux/input.h>
-#include <dbus/dbus.h>
+#include <fcntl.h>
+
+#include <string.h>
struct TypeCode {
unsigned type;
return (unsigned long)type << 16 | (unsigned long)code;
}
- bool operator < ( const TypeCode other) const {
+ bool operator < ( const TypeCode & other) const {
return hashCode() < other.hashCode();
}
};
};
-class EventNameByType : public map<TypeCode,const char*> {
+static struct enbet {
+ unsigned type;
+ const char * name;
+} EVENT_NAMES_BY_EVENT_TYPE[] = {
+ { EV_SYN, "syn" },
+ { EV_KEY, "button" },
+ { EV_REL, "rel" },
+ { EV_ABS, "abs" },
+ { EV_MSC, "msc" },
+ { EV_SW, "button" },
+ { EV_LED, "led" },
+ { EV_SND, "snd" },
+ { EV_REP, "rep" },
+ { EV_FF, "ff" },
+ { EV_PWR, "pwr" },
+ { EV_FF_STATUS, "ff-status" }
+};
+
+
+class EventNameByEventType : public std::map<unsigned,const char*> {
+public:
+ EventNameByEventType() {
+ for( unsigned i = 0; i < sizeof(EVENT_NAMES_BY_EVENT_TYPE)/sizeof(EVENT_NAMES_BY_EVENT_TYPE[0]); i++ )
+ (*this)[EVENT_NAMES_BY_EVENT_TYPE[i].type] = EVENT_NAMES_BY_EVENT_TYPE[i].name;
+ }
+};
+static EventNameByEventType EVENT_NAME_BY_EVENT_TYPE;
+
+class EventNameByType : public std::map<TypeCode,const char*> {
public:
EventNameByType() {
for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
struct ltstr {
bool operator()(const char * s1, const char * s2 ) const {
- return strcmp( s1, s2 ) < 0;
+ return std::string(s1).compare( s2 ) < 0;
}
};
-class EventTypeByName : public map<const char *,TypeCode,ltstr> {
+class EventTypeByName : public std::map<const char *,TypeCode,ltstr> {
public:
EventTypeByName() {
for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
static EventTypeByName EVENT_TYPE_BY_NAME;
-FGLinuxInputDevice::FGLinuxInputDevice( string aName, string aDevname ) :
+FGLinuxInputDevice::FGLinuxInputDevice( std::string aName, std::string aDevname ) :
FGInputDevice(aName),
devname( aDevname ),
fd(-1)
{
}
+static inline bool bitSet( unsigned char * buf, unsigned bit )
+{
+ return (buf[bit/sizeof(unsigned char)/8] >> (bit%(sizeof(unsigned char)*8))) & 1;
+}
+
void FGLinuxInputDevice::Open()
{
if( fd != -1 ) return;
if( (fd = ::open( devname.c_str(), O_RDWR )) == -1 ) {
- throw exception();
+ throw std::exception();
+ }
+
+ if( GetGrab() && ioctl( fd, EVIOCGRAB, 2 ) == -1 ) {
+ SG_LOG( SG_INPUT, SG_WARN, "Can't grab " << devname << " for exclusive access" );
+ }
+
+ {
+ unsigned char buf[ABS_CNT/sizeof(unsigned char)/8];
+ // get axes maximums
+ if( ioctl( fd, EVIOCGBIT(EV_ABS,ABS_MAX), buf ) == -1 ) {
+ SG_LOG( SG_INPUT, SG_WARN, "Can't get abs-axes for " << devname );
+ } else {
+ for( unsigned i = 0; i < ABS_MAX; i++ ) {
+ if( bitSet( buf, i ) ) {
+ struct input_absinfo ai;
+ if( ioctl(fd, EVIOCGABS(i), &ai) == -1 ) {
+ SG_LOG( SG_INPUT, SG_WARN, "Can't get abs-axes maximums for " << devname );
+ continue;
+ }
+ absinfo[i] = ai;
+/*
+ SG_LOG( SG_INPUT, SG_INFO, "Axis #" << i <<
+ ": value=" << ai.value <<
+ ": minimum=" << ai.minimum <<
+ ": maximum=" << ai.maximum <<
+ ": fuzz=" << ai.fuzz <<
+ ": flat=" << ai.flat <<
+ ": resolution=" << ai.resolution );
+*/
+
+ // kick an initial event
+ struct input_event event;
+ event.type = EV_ABS;
+ event.code = i;
+ event.value = ai.value;
+ FGLinuxEventData eventData( event, 0, 0 );
+ eventData.value = Normalize( event );
+ HandleEvent(eventData);
+ }
+ }
+ }
+ }
+ {
+ unsigned char mask[KEY_CNT/sizeof(unsigned char)/8];
+ unsigned char flag[KEY_CNT/sizeof(unsigned char)/8];
+ memset(mask,0,sizeof(mask));
+ memset(flag,0,sizeof(flag));
+ if( ioctl( fd, EVIOCGKEY(sizeof(flag)), flag ) == -1 ||
+ ioctl( fd, EVIOCGBIT(EV_KEY, sizeof(mask)), mask ) == -1 ) {
+ SG_LOG( SG_INPUT, SG_WARN, "Can't get keys for " << devname );
+ } else {
+ for( unsigned i = 0; i < KEY_MAX; i++ ) {
+ if( bitSet( mask, i ) ) {
+ struct input_event event;
+ event.type = EV_KEY;
+ event.code = i;
+ event.value = bitSet(flag,i);
+ FGLinuxEventData eventData( event, 0, 0 );
+ HandleEvent(eventData);
+ }
+ }
+ }
+ }
+ {
+ unsigned char buf[SW_CNT/sizeof(unsigned char)/8];
+ if( ioctl( fd, EVIOCGSW(sizeof(buf)), buf ) == -1 ) {
+ SG_LOG( SG_INPUT, SG_WARN, "Can't get switches for " << devname );
+ } else {
+ for( unsigned i = 0; i < SW_MAX; i++ ) {
+ if( bitSet( buf, i ) ) {
+ struct input_event event;
+ event.type = EV_SW;
+ event.code = i;
+ event.value = 1;
+ FGLinuxEventData eventData( event, 0, 0 );
+ HandleEvent(eventData);
+ }
+ }
+ }
+ }
+}
+
+double FGLinuxInputDevice::Normalize( struct input_event & event )
+{
+ if( absinfo.count(event.code) > 0 ) {
+ const struct input_absinfo & ai = absinfo[(unsigned int)event.code];
+ if( ai.maximum == ai.minimum )
+ return 0.0;
+ return ((double)event.value-(double)ai.minimum)/((double)ai.maximum-(double)ai.minimum);
+ } else {
+ return (double)event.value;
}
}
void FGLinuxInputDevice::Close()
{
- if( fd != -1 ) ::close(fd);
+ if( fd != -1 ) {
+ if( GetGrab() && ioctl( fd, EVIOCGRAB, 0 ) != 0 ) {
+ SG_LOG( SG_INPUT, SG_WARN, "Can't ungrab " << devname );
+ }
+ ::close(fd);
+ }
fd = -1;
}
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 );
+ // event not known in translation tables
+ if( EVENT_NAME_BY_EVENT_TYPE.count(linuxEventData.type) == 0 ) {
+ // event type not known in translation tables
+ sprintf( ugly_buffer, "unknown-%u-%u", (unsigned)linuxEventData.type, (unsigned)linuxEventData.code );
+ return ugly_buffer;
+ }
+ sprintf( ugly_buffer, "%s-%u", EVENT_NAME_BY_EVENT_TYPE[linuxEventData.type], (unsigned)linuxEventData.code );
return ugly_buffer;
}
return EVENT_NAME_BY_TYPE[typeCode];
}
-void FGLinuxInputDevice::SetDevname( string name )
+void FGLinuxInputDevice::SetDevname( const std::string & name )
{
this->devname = name;
}
-FGLinuxEventInput::FGLinuxEventInput() :
- halcontext(NULL)
+FGLinuxEventInput::FGLinuxEventInput()
{
}
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()
+void FGLinuxEventInput::postinit()
{
- FGEventInput::init();
-
- DBusConnection * connection;
- DBusError dbus_error;
+ FGEventInput::postinit();
- 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;
- }
+ struct udev * udev = udev_new();
- halcontext = libhal_ctx_new();
+ struct udev_enumerate *enumerate = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(enumerate, "input");
+ udev_enumerate_scan_devices(enumerate);
+ struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
+ struct udev_list_entry *dev_list_entry;
- libhal_ctx_set_dbus_connection (halcontext, connection );
- dbus_error_init (&dbus_error);
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ const char * path = udev_list_entry_get_name(dev_list_entry);
+ struct udev_device *dev = udev_device_new_from_syspath(udev, path);
+ const char * node = udev_device_get_devnode(dev);
+
+ dev = udev_device_get_parent( dev );
+ const char * name = udev_device_get_sysattr_value(dev,"name");
- if( libhal_ctx_init( halcontext, &dbus_error )) {
+ SG_LOG(SG_INPUT,SG_DEBUG, "name=" << (name?name:"<null>") << ", node=" << (node?node:"<null>"));
+ if( name && node )
+ AddDevice( new FGLinuxInputDevice(name, node) );
- 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 );
+ udev_device_unref(dev);
+ }
- if( device != NULL ) libhal_free_string( device );
- if( product != NULL ) libhal_free_string( product );
+ udev_unref(udev);
}
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;
+ std::map<int,FGLinuxInputDevice*> devicesByFd;
+ std::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;
FGLinuxEventData eventData( event, dt, modifiers );
+ if( event.type == EV_ABS )
+ eventData.value = devicesByFd[fds[i].fd]->Normalize( event );
+
// let the FGInputDevice handle the data
devicesByFd[fds[i].fd]->HandleEvent( eventData );
}
}
}
}
+