1 // FGEventInput.cxx -- handle event driven input devices for the Linux O/S
3 // Written by Torsten Dreyer, started July 2009.
5 // Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "FGLinuxEventInput.hxx"
30 #include <linux/input.h>
31 #include <dbus/dbus.h>
39 inline unsigned long hashCode() const {
40 return (unsigned long)type << 16 | (unsigned long)code;
43 bool operator < ( const TypeCode other) const {
44 return hashCode() < other.hashCode();
48 // event to name translation table
49 // events are from include <linux/input.h>
51 static struct EventTypes {
52 struct TypeCode typeCode;
55 { { EV_SYN, SYN_REPORT }, "syn-report" },
56 { { EV_SYN, SYN_CONFIG }, "syn-config" },
59 { { EV_KEY, BTN_0 }, "button-0" },
60 { { EV_KEY, BTN_1 }, "button-1" },
61 { { EV_KEY, BTN_2 }, "button-2" },
62 { { EV_KEY, BTN_3 }, "button-3" },
63 { { EV_KEY, BTN_4 }, "button-4" },
64 { { EV_KEY, BTN_5 }, "button-5" },
65 { { EV_KEY, BTN_6 }, "button-6" },
66 { { EV_KEY, BTN_7 }, "button-7" },
67 { { EV_KEY, BTN_8 }, "button-8" },
68 { { EV_KEY, BTN_9 }, "button-9" },
71 { { EV_KEY, BTN_LEFT }, "button-left" },
72 { { EV_KEY, BTN_RIGHT }, "button-right" },
73 { { EV_KEY, BTN_MIDDLE }, "button-middle" },
74 { { EV_KEY, BTN_SIDE }, "button-side" },
75 { { EV_KEY, BTN_EXTRA }, "button-extra" },
76 { { EV_KEY, BTN_FORWARD }, "button-forward" },
77 { { EV_KEY, BTN_BACK }, "button-back" },
78 { { EV_KEY, BTN_TASK }, "button-task" },
81 { { EV_KEY, BTN_TRIGGER }, "button-trigger" },
82 { { EV_KEY, BTN_THUMB }, "button-thumb" },
83 { { EV_KEY, BTN_THUMB2 }, "button-thumb2" },
84 { { EV_KEY, BTN_TOP }, "button-top" },
85 { { EV_KEY, BTN_TOP2 }, "button-top2" },
86 { { EV_KEY, BTN_PINKIE }, "button-pinkie" },
87 { { EV_KEY, BTN_BASE }, "button-base" },
88 { { EV_KEY, BTN_BASE2 }, "button-base2" },
89 { { EV_KEY, BTN_BASE3 }, "button-base3" },
90 { { EV_KEY, BTN_BASE4 }, "button-base4" },
91 { { EV_KEY, BTN_BASE5 }, "button-base5" },
92 { { EV_KEY, BTN_BASE6 }, "button-base6" },
93 { { EV_KEY, BTN_DEAD }, "button-dead" },
96 { { EV_KEY, BTN_A }, "button-a" },
97 { { EV_KEY, BTN_B }, "button-b" },
98 { { EV_KEY, BTN_C }, "button-c" },
99 { { EV_KEY, BTN_X }, "button-x" },
100 { { EV_KEY, BTN_Y }, "button-y" },
101 { { EV_KEY, BTN_Z }, "button-z" },
102 { { EV_KEY, BTN_TL }, "button-tl" },
103 { { EV_KEY, BTN_TR }, "button-tr" },
104 { { EV_KEY, BTN_TL2 }, "button-tl2" },
105 { { EV_KEY, BTN_TR2 }, "button-tr2" },
106 { { EV_KEY, BTN_SELECT }, "button-select" },
107 { { EV_KEY, BTN_START }, "button-start" },
108 { { EV_KEY, BTN_MODE }, "button-mode" },
109 { { EV_KEY, BTN_THUMBL }, "button-thumbl" },
110 { { EV_KEY, BTN_THUMBR }, "button-thumbr" },
113 { { EV_KEY, BTN_TOOL_PEN }, "button-pen" },
114 { { EV_KEY, BTN_TOOL_RUBBER }, "button-rubber" },
115 { { EV_KEY, BTN_TOOL_BRUSH }, "button-brush" },
116 { { EV_KEY, BTN_TOOL_PENCIL }, "button-pencil" },
117 { { EV_KEY, BTN_TOOL_AIRBRUSH }, "button-airbrush" },
118 { { EV_KEY, BTN_TOOL_FINGER }, "button-finger" },
119 { { EV_KEY, BTN_TOOL_MOUSE }, "button-mouse" },
120 { { EV_KEY, BTN_TOOL_LENS }, "button-lens" },
121 { { EV_KEY, BTN_TOUCH }, "button-touch" },
122 { { EV_KEY, BTN_STYLUS }, "button-stylus" },
123 { { EV_KEY, BTN_STYLUS2 }, "button-stylus2" },
124 { { EV_KEY, BTN_TOOL_DOUBLETAP }, "button-doubletap" },
125 { { EV_KEY, BTN_TOOL_TRIPLETAP }, "button-trippletap" },
127 { { EV_KEY, BTN_WHEEL }, "button-wheel" },
128 { { EV_KEY, BTN_GEAR_DOWN }, "button-gear-down" },
129 { { EV_KEY, BTN_GEAR_UP }, "button-gear-up" },
131 { { EV_REL, REL_X }, "rel-x-translate" },
132 { { EV_REL, REL_Y}, "rel-y-translate" },
133 { { EV_REL, REL_Z}, "rel-z-translate" },
134 { { EV_REL, REL_RX}, "rel-x-rotate" },
135 { { EV_REL, REL_RY}, "rel-y-rotate" },
136 { { EV_REL, REL_RZ}, "rel-z-rotate" },
137 { { EV_REL, REL_HWHEEL}, "rel-hwheel" },
138 { { EV_REL, REL_DIAL}, "rel-dial" },
139 { { EV_REL, REL_WHEEL}, "rel-wheel" },
140 { { EV_REL, REL_MISC}, "rel-misc" },
142 { { EV_ABS, ABS_X }, "abs-x-translate" },
143 { { EV_ABS, ABS_Y }, "abs-y-translate" },
144 { { EV_ABS, ABS_Z }, "abs-z-translate" },
145 { { EV_ABS, ABS_RX }, "abs-x-rotate" },
146 { { EV_ABS, ABS_RY }, "abs-y-rotate" },
147 { { EV_ABS, ABS_RZ }, "abs-z-rotate" },
148 { { EV_ABS, ABS_THROTTLE }, "abs-throttle" },
149 { { EV_ABS, ABS_RUDDER }, "abs-rudder" },
150 { { EV_ABS, ABS_WHEEL }, "abs-wheel" },
151 { { EV_ABS, ABS_GAS }, "abs-gas" },
152 { { EV_ABS, ABS_BRAKE }, "abs-brake" },
153 { { EV_ABS, ABS_HAT0X }, "abs-hat0-x" },
154 { { EV_ABS, ABS_HAT0Y }, "abs-hat0-y" },
155 { { EV_ABS, ABS_HAT1X }, "abs-hat1-x" },
156 { { EV_ABS, ABS_HAT1Y }, "abs-hat1-y" },
157 { { EV_ABS, ABS_HAT2X }, "abs-hat2-x" },
158 { { EV_ABS, ABS_HAT2Y }, "abs-hat2-y" },
159 { { EV_ABS, ABS_HAT3X }, "abs-hat3-x" },
160 { { EV_ABS, ABS_HAT3Y }, "abs-hat3-y" },
161 { { EV_ABS, ABS_PRESSURE }, "abs-pressure" },
162 { { EV_ABS, ABS_DISTANCE }, "abs-distance" },
163 { { EV_ABS, ABS_TILT_X }, "abs-tilt-x" },
164 { { EV_ABS, ABS_TILT_Y }, "abs-tilt-y" },
165 { { EV_ABS, ABS_TOOL_WIDTH }, "abs-toold-width" },
166 { { EV_ABS, ABS_VOLUME }, "abs-volume" },
167 { { EV_ABS, ABS_MISC }, "abs-misc" },
169 { { EV_MSC, MSC_SERIAL }, "misc-serial" },
170 { { EV_MSC, MSC_PULSELED }, "misc-pulseled" },
171 { { EV_MSC, MSC_GESTURE }, "misc-gesture" },
172 { { EV_MSC, MSC_RAW }, "misc-raw" },
173 { { EV_MSC, MSC_SCAN }, "misc-scan" },
176 { { EV_SW, SW_LID }, "switch-lid" },
177 { { EV_SW, SW_TABLET_MODE }, "switch-tablet-mode" },
178 { { EV_SW, SW_HEADPHONE_INSERT }, "switch-headphone-insert" },
180 { { EV_SW, SW_RFKILL_ALL }, "switch-rfkill" },
182 #ifdef SW_MICROPHONE_INSERT
183 { { EV_SW, SW_MICROPHONE_INSERT }, "switch-microphone-insert" },
186 { { EV_SW, SW_DOCK }, "switch-dock" },
189 { { EV_LED, LED_NUML}, "led-numlock" },
190 { { EV_LED, LED_CAPSL}, "led-capslock" },
191 { { EV_LED, LED_SCROLLL}, "led-scrolllock" },
192 { { EV_LED, LED_COMPOSE}, "led-compose" },
193 { { EV_LED, LED_KANA}, "led-kana" },
194 { { EV_LED, LED_SLEEP}, "led-sleep" },
195 { { EV_LED, LED_SUSPEND}, "led-suspend" },
196 { { EV_LED, LED_MUTE}, "led-mute" },
197 { { EV_LED, LED_MISC}, "led-misc" },
198 { { EV_LED, LED_MAIL}, "led-mail" },
199 { { EV_LED, LED_CHARGING}, "led-charging" }
203 static struct enbet {
206 } EVENT_NAMES_BY_EVENT_TYPE[] = {
208 { EV_KEY, "button" },
218 { EV_FF_STATUS, "ff-status" }
222 class EventNameByEventType : public map<unsigned,const char*> {
224 EventNameByEventType() {
225 for( unsigned i = 0; i < sizeof(EVENT_NAMES_BY_EVENT_TYPE)/sizeof(EVENT_NAMES_BY_EVENT_TYPE[0]); i++ )
226 (*this)[EVENT_NAMES_BY_EVENT_TYPE[i].type] = EVENT_NAMES_BY_EVENT_TYPE[i].name;
229 static EventNameByEventType EVENT_NAME_BY_EVENT_TYPE;
231 class EventNameByType : public map<TypeCode,const char*> {
234 for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
235 (*this)[EVENT_TYPES[i].typeCode] = EVENT_TYPES[i].name;
238 static EventNameByType EVENT_NAME_BY_TYPE;
242 bool operator()(const char * s1, const char * s2 ) const {
243 return string(s1).compare( s2 ) < 0;
247 class EventTypeByName : public map<const char *,TypeCode,ltstr> {
250 for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
251 (*this)[EVENT_TYPES[i].name] = EVENT_TYPES[i].typeCode;
254 static EventTypeByName EVENT_TYPE_BY_NAME;
257 FGLinuxInputDevice::FGLinuxInputDevice( string aName, string aDevname ) :
258 FGInputDevice(aName),
264 FGLinuxInputDevice::~FGLinuxInputDevice()
273 FGLinuxInputDevice::FGLinuxInputDevice() :
278 static inline bool bitSet( unsigned char * buf, unsigned char bit )
280 return (buf[bit/sizeof(bit)/8] >> (bit%(sizeof(bit)*8))) & 1;
283 void FGLinuxInputDevice::Open()
285 if( fd != -1 ) return;
286 if( (fd = ::open( devname.c_str(), O_RDWR )) == -1 ) {
290 if( GetGrab() && ioctl( fd, EVIOCGRAB, 2 ) == -1 ) {
291 SG_LOG( SG_INPUT, SG_WARN, "Can't grab " << devname << " for exclusive access" );
294 unsigned char buf[ABS_CNT/sizeof(unsigned char)/8];
296 if( ioctl( fd, EVIOCGBIT(EV_ABS,ABS_MAX), buf ) == -1 ) {
297 SG_LOG( SG_INPUT, SG_WARN, "Can't get abs-axes for " << devname );
299 for( unsigned i = 0; i < ABS_MAX; i++ ) {
300 if( bitSet( buf, i ) ) {
301 struct input_absinfo ai;
302 if( ioctl(fd, EVIOCGABS(i), &ai) == -1 ) {
303 SG_LOG( SG_INPUT, SG_WARN, "Can't get abs-axes maximums for " << devname );
312 double FGLinuxInputDevice::Normalize( struct input_event & event )
314 if( absinfo.count(event.code) > 0 ) {
315 const struct input_absinfo & ai = absinfo[(unsigned int)event.code];
316 if( ai.maximum == ai.minimum )
318 return ((double)event.value-(double)ai.minimum)/((double)ai.maximum-(double)ai.minimum);
320 return (double)event.value;
324 void FGLinuxInputDevice::Close()
327 if( GetGrab() && ioctl( fd, EVIOCGRAB, 0 ) != 0 ) {
328 SG_LOG( SG_INPUT, SG_WARN, "Can't ungrab " << devname );
335 void FGLinuxInputDevice::Send( const char * eventName, double value )
337 if( EVENT_TYPE_BY_NAME.count( eventName ) <= 0 ) {
338 SG_LOG( SG_INPUT, SG_ALERT, "Can't send unknown event " << eventName );
342 TypeCode & typeCode = EVENT_TYPE_BY_NAME[ eventName ];
348 evt.type=typeCode.type;
349 evt.code = typeCode.code;
350 evt.value = (long)value;
352 evt.time.tv_usec = 0;
353 write( fd, &evt, sizeof(evt) );
354 SG_LOG( SG_INPUT, SG_DEBUG, "Written event " << eventName
355 << " as type=" << evt.type << ", code=" << evt.code << " value=" << evt.value );
358 static char ugly_buffer[128];
359 const char * FGLinuxInputDevice::TranslateEventName( FGEventData & eventData )
361 FGLinuxEventData & linuxEventData = (FGLinuxEventData&)eventData;
363 typeCode.type = linuxEventData.type;
364 typeCode.code = linuxEventData.code;
365 if( EVENT_NAME_BY_TYPE.count(typeCode) == 0 ) {
366 // event not known in translation tables
367 if( EVENT_NAME_BY_EVENT_TYPE.count(linuxEventData.type) == 0 ) {
368 // event type not known in translation tables
369 sprintf( ugly_buffer, "unknown-%u-%u", (unsigned)linuxEventData.type, (unsigned)linuxEventData.code );
372 sprintf( ugly_buffer, "%s-%u", EVENT_NAME_BY_EVENT_TYPE[linuxEventData.type], (unsigned)linuxEventData.code );
376 return EVENT_NAME_BY_TYPE[typeCode];
379 void FGLinuxInputDevice::SetDevname( string name )
381 this->devname = name;
384 FGLinuxEventInput::FGLinuxEventInput() :
389 FGLinuxEventInput::~FGLinuxEventInput()
391 if( halcontext != NULL ) {
392 libhal_ctx_shutdown( halcontext, NULL);
393 libhal_ctx_free( halcontext );
399 //TODO: enable hotplug support
400 static void DeviceAddedCallback (LibHalContext *ctx, const char *udi)
402 FGLinuxEventInput * linuxEventInput = (FGLinuxEventInput*)libhal_ctx_get_user_data (ctx);
403 linuxEventInput->AddHalDevice( udi );
406 static void DeviceRemovedCallback (LibHalContext *ctx, const char *udi)
411 void FGLinuxEventInput::postinit()
413 FGEventInput::postinit();
415 DBusConnection * connection;
416 DBusError dbus_error;
418 dbus_error_init(&dbus_error);
419 connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
420 if (dbus_error_is_set(&dbus_error)) {
421 SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to system bus " << dbus_error.message);
422 dbus_error_free (&dbus_error);
426 halcontext = libhal_ctx_new();
428 libhal_ctx_set_dbus_connection (halcontext, connection );
429 dbus_error_init (&dbus_error);
431 if( libhal_ctx_init( halcontext, &dbus_error )) {
434 char ** devices = libhal_find_device_by_capability(halcontext, "input", &num_devices, NULL);
436 for ( int i = 0; i < num_devices; i++)
437 AddHalDevice( devices[i] );
439 libhal_free_string_array (devices);
441 //TODO: enable hotplug support
442 // libhal_ctx_set_user_data( halcontext, this );
443 // libhal_ctx_set_device_added( halcontext, DeviceAddedCallback );
444 // libhal_ctx_set_device_removed( halcontext, DeviceRemovedCallback );
446 if(dbus_error_is_set (&dbus_error) ) {
447 SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to hald: " << dbus_error.message);
448 dbus_error_free (&dbus_error);
450 SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to hald." );
453 libhal_ctx_free (halcontext);
458 void FGLinuxEventInput::AddHalDevice( const char * udi )
460 char * device = libhal_device_get_property_string( halcontext, udi, "input.device", NULL);
461 char * product = libhal_device_get_property_string( halcontext, udi, "input.product", NULL);
463 if( product != NULL && device != NULL )
464 AddDevice( new FGLinuxInputDevice(product, device) );
466 SG_LOG( SG_INPUT, SG_ALERT, "Can't get device or product property of " << udi );
468 if( device != NULL ) libhal_free_string( device );
469 if( product != NULL ) libhal_free_string( product );
473 void FGLinuxEventInput::update( double dt )
475 FGEventInput::update( dt );
476 // index the input devices by the associated fd and prepare
477 // the pollfd array by filling in the file descriptor
478 struct pollfd fds[input_devices.size()];
479 map<int,FGLinuxInputDevice*> devicesByFd;
480 map<int,FGInputDevice*>::const_iterator it;
482 for( i=0, it = input_devices.begin(); it != input_devices.end(); ++it, i++ ) {
483 FGInputDevice* p = (*it).second;
484 int fd = ((FGLinuxInputDevice*)p)->GetFd();
486 fds[i].events = POLLIN;
487 devicesByFd[fd] = (FGLinuxInputDevice*)p;
490 int modifiers = fgGetKeyModifiers();
491 // poll all devices until no more events are in the queue
492 // do no more than maxpolls in a single loop to prevent locking
494 while( maxpolls-- > 0 && ::poll( fds, i, 0 ) > 0 ) {
495 for( unsigned i = 0; i < sizeof(fds)/sizeof(fds[0]); i++ ) {
496 if( fds[i].revents & POLLIN ) {
498 // if this device reports data ready, it should be a input_event struct
499 struct input_event event;
501 if( read( fds[i].fd, &event, sizeof(event) ) != sizeof(event) )
504 FGLinuxEventData eventData( event, dt, modifiers );
506 if( event.type == EV_ABS )
507 eventData.value = devicesByFd[fds[i].fd]->Normalize( event );
509 // let the FGInputDevice handle the data
510 devicesByFd[fds[i].fd]->HandleEvent( eventData );