]> git.mxchange.org Git - flightgear.git/blob - src/Input/FGLinuxEventInput.cxx
Merge branch 'jsd/atmos' into topic/atmos-merge
[flightgear.git] / src / Input / FGLinuxEventInput.cxx
1 // FGEventInput.cxx -- handle event driven input devices for the Linux O/S
2 //
3 // Written by Torsten Dreyer, started July 2009.
4 //
5 // Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include "FGLinuxEventInput.hxx"
28
29 #include <poll.h>
30 #include <linux/input.h>
31 #include <dbus/dbus.h>
32
33 struct TypeCode {
34   unsigned type;
35   unsigned code;
36
37   inline unsigned long hashCode() const {
38     return (unsigned long)type << 16 | (unsigned long)code;
39   }
40
41   bool operator < ( const TypeCode other) const {
42     return hashCode() < other.hashCode();
43   }
44 };
45
46 // event to name translation table
47 // events are from include <linux/input.h>
48
49 static struct EventTypes {
50   struct TypeCode typeCode;
51   const char * name;
52 } EVENT_TYPES[] = {
53   { { EV_SYN, SYN_REPORT },     "syn-report" },
54   { { EV_SYN, SYN_CONFIG },     "syn-config" },
55
56   // misc
57   { { EV_KEY, BTN_0 }, "button-0" },
58   { { EV_KEY, BTN_1 }, "button-1" },
59   { { EV_KEY, BTN_2 }, "button-2" },
60   { { EV_KEY, BTN_3 }, "button-3" },
61   { { EV_KEY, BTN_4 }, "button-4" },
62   { { EV_KEY, BTN_5 }, "button-5" },
63   { { EV_KEY, BTN_6 }, "button-6" },
64   { { EV_KEY, BTN_7 }, "button-7" },
65   { { EV_KEY, BTN_8 }, "button-8" },
66   { { EV_KEY, BTN_9 }, "button-9" },
67
68   // mouse
69   { { EV_KEY, BTN_LEFT },    "button-left" },
70   { { EV_KEY, BTN_RIGHT },   "button-right" },
71   { { EV_KEY, BTN_MIDDLE },  "button-middle" },
72   { { EV_KEY, BTN_SIDE },    "button-side" },
73   { { EV_KEY, BTN_EXTRA },   "button-extra" },
74   { { EV_KEY, BTN_FORWARD }, "button-forward" },
75   { { EV_KEY, BTN_BACK },    "button-back" },
76   { { EV_KEY, BTN_TASK },    "button-task" },
77
78   // joystick
79   { { EV_KEY, BTN_TRIGGER }, "button-trigger" },
80   { { EV_KEY, BTN_THUMB },   "button-thumb" },
81   { { EV_KEY, BTN_THUMB2 },  "button-thumb2" },
82   { { EV_KEY, BTN_TOP },     "button-top" },
83   { { EV_KEY, BTN_TOP2 },    "button-top2" },
84   { { EV_KEY, BTN_PINKIE },  "button-pinkie" },
85   { { EV_KEY, BTN_BASE },    "button-base" },
86   { { EV_KEY, BTN_BASE2 },   "button-base2" },
87   { { EV_KEY, BTN_BASE3 },   "button-base3" },
88   { { EV_KEY, BTN_BASE4 },   "button-base4" },
89   { { EV_KEY, BTN_BASE5 },   "button-base5" },
90   { { EV_KEY, BTN_BASE6 },   "button-base6" },
91   { { EV_KEY, BTN_DEAD },    "button-dead" },
92
93   // gamepad
94   { { EV_KEY, BTN_A },      "button-a" },
95   { { EV_KEY, BTN_B },      "button-b" },
96   { { EV_KEY, BTN_C },      "button-c" },
97   { { EV_KEY, BTN_X },      "button-x" },
98   { { EV_KEY, BTN_Y },      "button-y" },
99   { { EV_KEY, BTN_Z },      "button-z" },
100   { { EV_KEY, BTN_TL },     "button-tl" },
101   { { EV_KEY, BTN_TR },     "button-tr" },
102   { { EV_KEY, BTN_TL2 },    "button-tl2" },
103   { { EV_KEY, BTN_TR2 },    "button-tr2" },
104   { { EV_KEY, BTN_SELECT }, "button-select" },
105   { { EV_KEY, BTN_START },  "button-start" },
106   { { EV_KEY, BTN_MODE },   "button-mode" },
107   { { EV_KEY, BTN_THUMBL }, "button-thumbl" },
108   { { EV_KEY, BTN_THUMBR }, "button-thumbr" },
109
110   // digitizer
111   { { EV_KEY, BTN_TOOL_PEN },       "button-pen" },
112   { { EV_KEY, BTN_TOOL_RUBBER },    "button-rubber" },
113   { { EV_KEY, BTN_TOOL_BRUSH },     "button-brush" },
114   { { EV_KEY, BTN_TOOL_PENCIL },    "button-pencil" },
115   { { EV_KEY, BTN_TOOL_AIRBRUSH },  "button-airbrush" },
116   { { EV_KEY, BTN_TOOL_FINGER },    "button-finger" },
117   { { EV_KEY, BTN_TOOL_MOUSE },     "button-mouse" },
118   { { EV_KEY, BTN_TOOL_LENS },      "button-lens" },
119   { { EV_KEY, BTN_TOUCH },          "button-touch" },
120   { { EV_KEY, BTN_STYLUS },         "button-stylus" },
121   { { EV_KEY, BTN_STYLUS2 },        "button-stylus2" },
122   { { EV_KEY, BTN_TOOL_DOUBLETAP }, "button-doubletap" },
123   { { EV_KEY, BTN_TOOL_TRIPLETAP }, "button-trippletap" },
124
125   { { EV_KEY, BTN_WHEEL },          "button-wheel" },
126   { { EV_KEY, BTN_GEAR_DOWN },      "button-gear-down" },
127   { { EV_KEY, BTN_GEAR_UP },        "button-gear-up" },
128
129   { { EV_REL, REL_X },     "rel-x-translate" },
130   { { EV_REL, REL_Y},      "rel-y-translate" },
131   { { EV_REL, REL_Z},      "rel-z-translate" },
132   { { EV_REL, REL_RX},     "rel-x-rotate" },
133   { { EV_REL, REL_RY},     "rel-y-rotate" },
134   { { EV_REL, REL_RZ},     "rel-z-rotate" },
135   { { EV_REL, REL_HWHEEL}, "rel-hwheel" },
136   { { EV_REL, REL_DIAL},   "rel-dial" },
137   { { EV_REL, REL_WHEEL},  "rel-wheel" },
138   { { EV_REL, REL_MISC},   "rel-misc" },
139
140   { { EV_ABS, ABS_X },          "abs-x-translate" },
141   { { EV_ABS, ABS_Y },          "abs-y-translate" },
142   { { EV_ABS, ABS_Z },          "abs-z-translate" },
143   { { EV_ABS, ABS_RX },         "abs-x-rotate" },
144   { { EV_ABS, ABS_RY },         "abs-y-rotate" },
145   { { EV_ABS, ABS_RZ },         "abs-z-rotate" },
146   { { EV_ABS, ABS_THROTTLE },   "abs-throttle" },
147   { { EV_ABS, ABS_RUDDER },     "abs-rudder" },
148   { { EV_ABS, ABS_WHEEL },      "abs-wheel" },
149   { { EV_ABS, ABS_GAS },        "abs-gas" },
150   { { EV_ABS, ABS_BRAKE },      "abs-brake" },
151   { { EV_ABS, ABS_HAT0X },      "abs-hat0-x" },
152   { { EV_ABS, ABS_HAT0Y },      "abs-hat0-y" },
153   { { EV_ABS, ABS_HAT1X },      "abs-hat1-x" },
154   { { EV_ABS, ABS_HAT1Y },      "abs-hat1-y" },
155   { { EV_ABS, ABS_HAT2X },      "abs-hat2-x" },
156   { { EV_ABS, ABS_HAT2Y },      "abs-hat2-y" },
157   { { EV_ABS, ABS_HAT3X },      "abs-hat3-x" },
158   { { EV_ABS, ABS_HAT3Y },      "abs-hat3-y" },
159   { { EV_ABS, ABS_PRESSURE },   "abs-pressure" },
160   { { EV_ABS, ABS_DISTANCE },   "abs-distance" },
161   { { EV_ABS, ABS_TILT_X },     "abs-tilt-x" },
162   { { EV_ABS, ABS_TILT_Y },     "abs-tilt-y" },
163   { { EV_ABS, ABS_TOOL_WIDTH }, "abs-toold-width" },
164   { { EV_ABS, ABS_VOLUME },     "abs-volume" },
165   { { EV_ABS, ABS_MISC },       "abs-misc" },
166
167   { { EV_MSC,  MSC_SERIAL },    "misc-serial" },
168   { { EV_MSC,  MSC_PULSELED },  "misc-pulseled" },
169   { { EV_MSC,  MSC_GESTURE },   "misc-gesture" },
170   { { EV_MSC,  MSC_RAW },       "misc-raw" },
171   { { EV_MSC,  MSC_SCAN },      "misc-scan" },
172
173   // switch
174   { { EV_SW, SW_LID },               "switch-lid" },
175   { { EV_SW, SW_TABLET_MODE },       "switch-tablet-mode" },
176   { { EV_SW, SW_HEADPHONE_INSERT },  "switch-headphone-insert" },
177 #ifdef SW_RFKILL_ALL 
178   { { EV_SW, SW_RFKILL_ALL },        "switch-rfkill" },
179 #endif
180 #ifdef SW_MICROPHONE_INSERT 
181   { { EV_SW, SW_MICROPHONE_INSERT }, "switch-microphone-insert" },
182 #endif
183 #ifdef SW_DOCK
184   { { EV_SW, SW_DOCK },              "switch-dock" },
185 #endif
186
187   { { EV_LED, LED_NUML},     "led-numlock" },
188   { { EV_LED, LED_CAPSL},    "led-capslock" },
189   { { EV_LED, LED_SCROLLL},  "led-scrolllock" },
190   { { EV_LED, LED_COMPOSE},  "led-compose" },
191   { { EV_LED, LED_KANA},     "led-kana" },
192   { { EV_LED, LED_SLEEP},    "led-sleep" },
193   { { EV_LED, LED_SUSPEND},  "led-suspend" },
194   { { EV_LED, LED_MUTE},     "led-mute" },
195   { { EV_LED, LED_MISC},     "led-misc" },
196   { { EV_LED, LED_MAIL},     "led-mail" },
197   { { EV_LED, LED_CHARGING}, "led-charging" }
198
199 };
200
201 static struct enbet {
202   unsigned type;
203   const char * name;
204 } EVENT_NAMES_BY_EVENT_TYPE[] = {
205   { EV_SYN, "syn" },
206   { EV_KEY, "button" },
207   { EV_REL, "rel" },
208   { EV_ABS, "abs" },
209   { EV_MSC, "msc" },
210   { EV_SW, "button" },
211   { EV_LED, "led" },
212   { EV_SND, "snd" },
213   { EV_REP, "rep" },
214   { EV_FF, "ff" },
215   { EV_PWR, "pwr" },
216   { EV_FF_STATUS, "ff-status" }
217 };
218
219
220 class EventNameByEventType : public map<unsigned,const char*> {
221 public:
222   EventNameByEventType() {
223     for( unsigned i = 0; i < sizeof(EVENT_NAMES_BY_EVENT_TYPE)/sizeof(EVENT_NAMES_BY_EVENT_TYPE[0]); i++ )
224       (*this)[EVENT_NAMES_BY_EVENT_TYPE[i].type] = EVENT_NAMES_BY_EVENT_TYPE[i].name;
225   }
226 };
227 static EventNameByEventType EVENT_NAME_BY_EVENT_TYPE;
228
229 class EventNameByType : public map<TypeCode,const char*> {
230 public:
231   EventNameByType() {
232     for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
233       (*this)[EVENT_TYPES[i].typeCode] = EVENT_TYPES[i].name;
234   }
235 };
236 static EventNameByType EVENT_NAME_BY_TYPE;
237
238
239 struct ltstr {
240   bool operator()(const char * s1, const char * s2 ) const {
241     return strcmp( s1, s2 ) < 0;
242   }
243 };
244
245 class EventTypeByName : public map<const char *,TypeCode,ltstr> {
246 public:
247   EventTypeByName() {
248     for( unsigned i = 0; i < sizeof(EVENT_TYPES)/sizeof(EVENT_TYPES[0]); i++ )
249       (*this)[EVENT_TYPES[i].name] = EVENT_TYPES[i].typeCode;
250   }
251 };
252 static EventTypeByName EVENT_TYPE_BY_NAME;
253
254
255 FGLinuxInputDevice::FGLinuxInputDevice( string aName, string aDevname ) :
256   FGInputDevice(aName),
257   devname( aDevname ),
258   fd(-1)
259 {
260 }
261
262 FGLinuxInputDevice::~FGLinuxInputDevice()
263 {
264   try {
265     Close();
266   } 
267   catch(...) {
268   }
269 }
270
271 FGLinuxInputDevice::FGLinuxInputDevice() :
272   fd(-1)
273 {
274 }
275
276 void FGLinuxInputDevice::Open()
277 {
278   if( fd != -1 ) return;
279   if( (fd = ::open( devname.c_str(), O_RDWR )) == -1 ) { 
280     throw exception();
281   }
282
283   if( GetGrab() && ioctl( fd, EVIOCGRAB, 2 ) != 0 ) {
284     SG_LOG( SG_INPUT, SG_WARN, "Can't grab " << devname << " for exclusive access" );
285   }
286 }
287
288 void FGLinuxInputDevice::Close()
289 {
290   if( fd != -1 ) {
291     if( GetGrab() && ioctl( fd, EVIOCGRAB, 0 ) != 0 ) {
292       SG_LOG( SG_INPUT, SG_WARN, "Can't ungrab " << devname );
293     }
294     ::close(fd);
295   }
296   fd = -1;
297 }
298
299 void FGLinuxInputDevice::Send( const char * eventName, double value )
300 {
301   if( EVENT_TYPE_BY_NAME.count( eventName ) <= 0 ) {
302     SG_LOG( SG_INPUT, SG_ALERT, "Can't send unknown event " << eventName );
303     return;
304   }
305
306   TypeCode & typeCode = EVENT_TYPE_BY_NAME[ eventName ];
307
308   if( fd == -1 )
309     return;
310
311   input_event evt;
312   evt.type=typeCode.type;
313   evt.code = typeCode.code;
314   evt.value = (long)value;
315   evt.time.tv_sec = 0;
316   evt.time.tv_usec = 0;
317   write( fd, &evt, sizeof(evt) );
318   SG_LOG( SG_INPUT, SG_DEBUG, "Written event " << eventName 
319           << " as type=" << evt.type << ", code=" << evt.code << " value=" << evt.value );
320 }
321
322 static char ugly_buffer[128];
323 const char * FGLinuxInputDevice::TranslateEventName( FGEventData & eventData ) 
324 {
325   FGLinuxEventData & linuxEventData = (FGLinuxEventData&)eventData;
326   TypeCode typeCode;
327   typeCode.type = linuxEventData.type;
328   typeCode.code = linuxEventData.code;
329   if( EVENT_NAME_BY_TYPE.count(typeCode) == 0 ) {
330     // event not known in translation tables
331     if( EVENT_NAME_BY_EVENT_TYPE.count(linuxEventData.type) == 0 ) {
332       // event type not known in translation tables
333       sprintf( ugly_buffer, "unknown-%u-%u", (unsigned)linuxEventData.type, (unsigned)linuxEventData.code );
334       return ugly_buffer;
335     }
336     sprintf( ugly_buffer, "%s-%u", EVENT_NAME_BY_EVENT_TYPE[linuxEventData.type], (unsigned)linuxEventData.code );
337     return ugly_buffer;
338   }
339
340   return EVENT_NAME_BY_TYPE[typeCode];
341 }
342
343 void FGLinuxInputDevice::SetDevname( string name )
344 {
345   this->devname = name; 
346 }
347
348 FGLinuxEventInput::FGLinuxEventInput() : 
349   halcontext(NULL)
350 {
351 }
352
353 FGLinuxEventInput::~FGLinuxEventInput()
354 {
355   if( halcontext != NULL ) {
356     libhal_ctx_shutdown( halcontext, NULL);
357     libhal_ctx_free( halcontext );
358     halcontext = NULL;
359   }
360 }
361
362 #if 0
363 //TODO: enable hotplug support
364 static void DeviceAddedCallback (LibHalContext *ctx, const char *udi)
365 {
366   FGLinuxEventInput * linuxEventInput = (FGLinuxEventInput*)libhal_ctx_get_user_data (ctx);
367   linuxEventInput->AddHalDevice( udi );
368 }
369
370 static void DeviceRemovedCallback (LibHalContext *ctx, const char *udi)
371 {
372 }
373 #endif
374
375 void FGLinuxEventInput::postinit()
376 {
377   FGEventInput::postinit();
378
379   DBusConnection * connection;
380   DBusError dbus_error;
381
382   dbus_error_init(&dbus_error);
383   connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
384   if (dbus_error_is_set(&dbus_error)) {
385     SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to system bus " << dbus_error.message);
386     dbus_error_free (&dbus_error);
387     return;
388   }
389
390   halcontext = libhal_ctx_new();
391
392   libhal_ctx_set_dbus_connection (halcontext, connection );
393   dbus_error_init (&dbus_error);
394
395   if( libhal_ctx_init( halcontext,  &dbus_error )) {
396
397       int num_devices = 0;
398       char ** devices = libhal_find_device_by_capability(halcontext, "input", &num_devices, NULL);
399
400       for ( int i = 0; i < num_devices; i++)
401         AddHalDevice( devices[i] );
402
403       libhal_free_string_array (devices);
404
405 //TODO: enable hotplug support
406 //      libhal_ctx_set_user_data( halcontext, this );
407 //      libhal_ctx_set_device_added( halcontext, DeviceAddedCallback );
408 //      libhal_ctx_set_device_removed( halcontext, DeviceRemovedCallback );
409     } else {
410       if(dbus_error_is_set (&dbus_error) ) {
411         SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to hald: " << dbus_error.message);
412         dbus_error_free (&dbus_error);
413       } else {
414         SG_LOG( SG_INPUT, SG_ALERT, "Can't connect to hald." );
415       }
416
417       libhal_ctx_free (halcontext);
418       halcontext = NULL;
419     }
420 }
421
422 void FGLinuxEventInput::AddHalDevice( const char * udi )
423 {
424   char * device = libhal_device_get_property_string( halcontext, udi, "input.device", NULL);
425   char * product = libhal_device_get_property_string( halcontext, udi, "input.product", NULL);
426
427   if( product != NULL && device != NULL ) 
428     AddDevice( new FGLinuxInputDevice(product, device) );
429   else
430     SG_LOG( SG_INPUT, SG_ALERT, "Can't get device or product property of " << udi );
431
432   if( device != NULL ) libhal_free_string( device );
433   if( product != NULL ) libhal_free_string( product );
434
435 }
436
437 void FGLinuxEventInput::update( double dt )
438 {
439   FGEventInput::update( dt );
440
441   // index the input devices by the associated fd and prepare
442   // the pollfd array by filling in the file descriptor
443   struct pollfd fds[input_devices.size()];
444   map<int,FGLinuxInputDevice*> devicesByFd;
445   map<int,FGInputDevice*>::const_iterator it;
446   int i;
447   for( i=0, it = input_devices.begin(); it != input_devices.end(); ++it, i++ ) {
448     FGInputDevice* p = (*it).second;
449     int fd = ((FGLinuxInputDevice*)p)->GetFd();
450     fds[i].fd = fd;
451     fds[i].events = POLLIN;
452     devicesByFd[fd] = (FGLinuxInputDevice*)p;
453   }
454
455   int modifiers = fgGetKeyModifiers();
456   // poll all devices until no more events are in the queue
457   // do no more than maxpolls in a single loop to prevent locking
458   int maxpolls = 100;
459   while( maxpolls-- > 0 && ::poll( fds, i, 0 ) > 0 ) {
460     for( unsigned i = 0; i < sizeof(fds)/sizeof(fds[0]); i++ ) {
461       if( fds[i].revents & POLLIN ) {
462
463         // if this device reports data ready, it should be a input_event struct
464         struct input_event event;
465
466         if( read( fds[i].fd, &event, sizeof(event) ) != sizeof(event) )
467           continue;
468
469         FGLinuxEventData eventData( event, dt, modifiers );
470
471         // let the FGInputDevice handle the data
472         devicesByFd[fds[i].fd]->HandleEvent( eventData );
473       }
474     }
475   }
476 }