X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FInput%2FFGLinuxEventInput.cxx;h=39702cfc75998bfd91c60f100152bd9099252e5f;hb=5151f7f5e0ba7c7e6688bdfbc4cea365196e1ed3;hp=eb191f52402008a12aff0df126b3cffd54f0790a;hpb=3231561e5aab236f53c7d528b9ebd153198e2dd9;p=flightgear.git diff --git a/src/Input/FGLinuxEventInput.cxx b/src/Input/FGLinuxEventInput.cxx index eb191f524..39702cfc7 100644 --- a/src/Input/FGLinuxEventInput.cxx +++ b/src/Input/FGLinuxEventInput.cxx @@ -17,18 +17,26 @@ // 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 +#include +#include +#include #include "FGLinuxEventInput.hxx" +extern "C" { + #include +} + #include #include -#include +#include + +#include struct TypeCode { unsigned type; @@ -38,7 +46,7 @@ struct TypeCode { return (unsigned long)type << 16 | (unsigned long)code; } - bool operator < ( const TypeCode other) const { + bool operator < ( const TypeCode & other) const { return hashCode() < other.hashCode(); } }; @@ -198,10 +206,38 @@ static struct EventTypes { }; -class EventNameByType : public map { +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 { +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 { public: EventNameByType() { - for( int i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ ) + for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ ) (*this)[EVENT_TYPES[i].typeCode] = EVENT_TYPES[i].name; } }; @@ -210,25 +246,24 @@ static EventNameByType EVENT_NAME_BY_TYPE; 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 { +class EventTypeByName : public std::map { public: EventTypeByName() { - for( int i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ ) + for( unsigned 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 ) : +FGLinuxInputDevice::FGLinuxInputDevice( std::string aName, std::string aDevname ) : FGInputDevice(aName), - fd(-1), - devname( aDevname ) - + devname( aDevname ), + fd(-1) { } @@ -246,26 +281,118 @@ FGLinuxInputDevice::FGLinuxInputDevice() : { } +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; /* - 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) ); + 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; } @@ -287,9 +414,18 @@ void FGLinuxInputDevice::Send( const char * eventName, double value ) 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 ); + size_t bytes_written = write(fd, &evt, sizeof(evt)); + + if( bytes_written == sizeof(evt) ) + SG_LOG( SG_INPUT, + SG_DEBUG, + "Written event " << eventName << " as type=" << evt.type + << ", code=" << evt.code + << " value=" << evt.value ); + else + SG_LOG( SG_INPUT, + SG_WARN, + "Failed to write event: written = " << bytes_written ); } static char ugly_buffer[128]; @@ -300,116 +436,71 @@ const char * FGLinuxInputDevice::TranslateEventName( FGEventData & eventData ) 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) +void FGLinuxEventInput::postinit() { - 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; + 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; - } - - halcontext = libhal_ctx_new(); - - libhal_ctx_set_dbus_connection (halcontext, connection ); - dbus_error_init (&dbus_error); - - if( libhal_ctx_init( halcontext, &dbus_error )) { + struct udev * udev = udev_new(); - 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." ); - } + 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_free (halcontext); - halcontext = NULL; - } -} + 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"); -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); + SG_LOG(SG_INPUT,SG_DEBUG, "name=" << (name?name:"") << ", node=" << (node?node:"")); + if( name && node ) + AddDevice( new FGLinuxInputDevice(name, node) ); - 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 devicesByFd; - map::const_iterator it; + std::map devicesByFd; + std::map::const_iterator it; int i; for( i=0, it = input_devices.begin(); it != input_devices.end(); ++it, i++ ) { FGInputDevice* p = (*it).second; @@ -424,7 +515,7 @@ void FGLinuxEventInput::update( double dt ) // 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++ ) { + for( unsigned 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 @@ -435,9 +526,13 @@ void FGLinuxEventInput::update( double dt ) 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 ); } } } } +