FlightGear next
FGEventInput.cxx
Go to the documentation of this file.
1// FGEventInput.cxx -- handle event driven input devices
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 <cstring>
28#include "FGEventInput.hxx"
29#include <Main/fg_props.hxx>
30#include <simgear/io/sg_file.hxx>
31#include <simgear/props/props_io.hxx>
32#include <simgear/math/SGMath.hxx>
33#include <simgear/math/interpolater.hxx>
35
36using simgear::PropertyList;
37using std::cout;
38using std::endl;
39using std::map;
40using std::string;
41
42FGEventSetting::FGEventSetting( SGPropertyNode_ptr base ) :
43 value(0.0)
44{
45 SGPropertyNode_ptr n;
46
47 if( (n = base->getNode( "value" )) != NULL ) {
48 valueNode = NULL;
49 value = n->getDoubleValue();
50 } else {
51 n = base->getNode( "property" );
52 if( n == NULL ) {
53 SG_LOG( SG_INPUT, SG_WARN, "Neither <value> nor <property> defined for event setting." );
54 } else {
55 valueNode = fgGetNode( n->getStringValue(), true );
56 }
57 }
58
59 if( (n = base->getChild("condition")) != NULL )
60 condition = sgReadCondition(base, n);
61 else
62 SG_LOG( SG_INPUT, SG_ALERT, "No condition for event setting." );
63}
64
66{
67 return valueNode == NULL ? value : valueNode->getDoubleValue();
68}
69
71{
72 return condition == NULL ? true : condition->test();
73}
74
75static inline bool StartsWith( string & s, const char * cp )
76{
77 return s.find( cp ) == 0;
78}
79
80FGInputEvent * FGInputEvent::NewObject( FGInputDevice * device, SGPropertyNode_ptr eventNode )
81{
82 string name = eventNode->getStringValue( "name", "" );
83 if( StartsWith( name, "button-" ) )
84 return new FGButtonEvent( device, eventNode );
85
86 if( StartsWith( name, "rel-" ) )
87 return new FGRelAxisEvent( device, eventNode );
88
89 if( StartsWith( name, "abs-" ) )
90 return new FGAbsAxisEvent( device, eventNode );
91
92 return new FGInputEvent( device, eventNode );
93}
94
95FGInputEvent::FGInputEvent( FGInputDevice * aDevice, SGPropertyNode_ptr eventNode ) :
96 device( aDevice ),
97 lastDt(0.0),
98 lastSettingValue(std::numeric_limits<float>::quiet_NaN())
99{
100 name = eventNode->getStringValue( "name", "" );
101 desc = eventNode->getStringValue( "desc", "" );
102 intervalSec = eventNode->getDoubleValue("interval-sec",0.0);
103
104 read_bindings( eventNode, bindings, KEYMOD_NONE, device->GetNasalModule() );
105
106 for (auto child : eventNode->getChildren("setting"))
107 settings.push_back( new FGEventSetting(child) );
108}
109
113
114// send changed value to device (if condition matches)
115void FGInputEvent::update( double dt )
116{
117 for (auto setting : settings) {
118 if( setting->Test() ) {
119 const double value = setting->GetValue();
120 if( value != lastSettingValue ) {
121 device->Send( GetName(), value );
122 lastSettingValue = value;
123 }
124 }
125 }
126}
127
129{
130 lastDt += eventData.dt;
131 if( lastDt >= intervalSec ) {
132
133 for( binding_list_t::iterator it = bindings[eventData.modifiers].begin(); it != bindings[eventData.modifiers].end(); ++it )
134 fire( *it, eventData );
135
137 }
138}
139
140void FGInputEvent::fire(SGAbstractBinding* binding, FGEventData& eventData)
141{
142 binding->fire();
143}
144
145
146
147FGAxisEvent::FGAxisEvent( FGInputDevice * device, SGPropertyNode_ptr eventNode ) :
148 FGInputEvent( device, eventNode )
149{
150 tolerance = eventNode->getDoubleValue("tolerance", 0.002);
151 minRange = eventNode->getDoubleValue("min-range", 0.0 );
152 maxRange = eventNode->getDoubleValue("max-range", 0.0 );
153 center = eventNode->getDoubleValue("center", 0.0);
154 deadband = eventNode->getDoubleValue("dead-band", 0.0);
155 lowThreshold = eventNode->getDoubleValue("low-threshold", -0.9);
156 highThreshold = eventNode->getDoubleValue("high-threshold", 0.9);
157 lastValue = 9999999;
158
159// interpolation of values
160 if (eventNode->hasChild("interpolater")) {
161 interpolater.reset(new SGInterpTable{eventNode->getChild("interpolater")});
162 mirrorInterpolater = eventNode->getBoolValue("interpolater/mirrored", false);
163 }
164}
165
169
171{
172 if (fabs( eventData.value - lastValue) < tolerance)
173 return;
174 lastValue = eventData.value;
175
176 // We need a copy of the FGEventData struct to set the new value and to avoid side effects
177 FGEventData ed = eventData;
178
179 if( minRange != maxRange )
180 ed.value = 2.0*(eventData.value-minRange)/(maxRange-minRange)-1.0;
181
182 if( fabs(ed.value) < deadband )
183 ed.value = 0.0;
184
185 if (interpolater) {
186 if ((ed.value < 0.0) && mirrorInterpolater) {
187 // mirror the positive interpolation for negative values
188 ed.value = -interpolater->interpolate(fabs(ed.value));
189 } else {
190 ed.value = interpolater->interpolate(ed.value);
191 }
192 }
193
194 FGInputEvent::fire( ed );
195}
196
197void FGAbsAxisEvent::fire(SGAbstractBinding* binding, FGEventData& eventData)
198{
199 // sets the "setting" node
200 binding->fire( eventData.value );
201}
202
203FGRelAxisEvent::FGRelAxisEvent( FGInputDevice * device, SGPropertyNode_ptr eventNode ) :
204 FGAxisEvent( device, eventNode )
205{
206 // relative axes can't use tolerance
207 tolerance = 0.0;
208}
209
210void FGRelAxisEvent::fire(SGAbstractBinding* binding, FGEventData& eventData)
211{
212 // sets the "offset" node
213 binding->fire( eventData.value, 1.0 );
214}
215
216FGButtonEvent::FGButtonEvent( FGInputDevice * device, SGPropertyNode_ptr eventNode ) :
217 FGInputEvent( device, eventNode ),
218 repeatable(false),
219 lastState(false)
220{
221 repeatable = eventNode->getBoolValue("repeatable", repeatable);
222}
223
225{
226 bool pressed = eventData.value > 0.0;
227 if (pressed) {
228 // The press event may be repeated.
229 if (!lastState || repeatable) {
230 SG_LOG( SG_INPUT, SG_DEBUG, "Button '" << this->name << "' has been pressed" );
231 FGInputEvent::fire( eventData );
232 }
233 } else {
234 // The release event is never repeated.
235 if (lastState) {
236 SG_LOG( SG_INPUT, SG_DEBUG, "Button '" << this->name << "' has been released" );
237 eventData.modifiers|=KEYMOD_RELEASED;
238 FGInputEvent::fire( eventData );
239 }
240 }
241
242 lastState = pressed;
243}
244
245void FGButtonEvent::update( double dt )
246{
247 if (repeatable && lastState) {
248 // interval / dt handling is done by base ::fire method
249 FGEventData ed{1.0, dt, 0 /* modifiers */};
250 FGInputEvent::fire( ed );
251 }
252}
253
255{
256 auto nas = globals->get_subsystem<FGNasalSys>();
257 if (nas && deviceNode ) {
258 SGPropertyNode_ptr nasal = deviceNode->getNode("nasal");
259 if( nasal ) {
260 SGPropertyNode_ptr nasalClose = nasal->getNode("close");
261 if (nasalClose) {
262 const string s = nasalClose->getStringValue();
263 nas->createModule(nasalModule.c_str(), nasalModule.c_str(), s.c_str(), s.length(), deviceNode );
264 }
265 }
266 nas->deleteModule(nasalModule.c_str());
267 }
268}
269
270void FGInputDevice::Configure( SGPropertyNode_ptr aDeviceNode )
271{
272 deviceNode = aDeviceNode;
273
274 // export our class_id to property tree
275 deviceNode->getNode("_class-id", true)->setValue(class_id.c_str());
276
277 SG_LOG(SG_INPUT, SG_DEBUG, "FGInputDevice::Configure");
278
279 // use _uniqueName here so each loaded device gets its own Nasal module
280 nasalModule = string("__event:") + _uniqueName;
281
282 for (auto ev : deviceNode->getChildren( "event" )) {
284 }
285
286 debugEvents = deviceNode->getBoolValue("debug-events", debugEvents );
287 grab = deviceNode->getBoolValue("grab", grab );
288
289 PropertyList reportNodes = deviceNode->getChildren("report");
290 for( PropertyList::iterator it = reportNodes.begin(); it != reportNodes.end(); ++it ) {
292 reportSettings.push_back(r);
293 }
294
295
296 lastEventName = deviceNode->getNode("last-event", true)->getNode("name", true);
297 lastEventName->setStringValue("");
298 lastEventValue = deviceNode->getNode("last-event")->getNode("value", true);
299 lastEventValue->setDoubleValue(0.0);
300
301 SGPropertyNode_ptr nasal = deviceNode->getNode("nasal");
302 if (nasal) {
303 SGPropertyNode_ptr open = nasal->getNode("open");
304 if (open) {
305 const string s = open->getStringValue();
306 auto nas = globals->get_subsystem<FGNasalSys>();
307 if (nas)
308 nas->createModule(nasalModule.c_str(), nasalModule.c_str(), s.c_str(), s.length(), deviceNode );
309 }
310 }
311}
312
314{
315 auto it = handledEvents.find(event->GetName());
316 if (it == handledEvents.end()) {
317 handledEvents.insert(it, std::make_pair(event->GetName(), event));
318 }
319}
320
321void FGInputDevice::update( double dt )
322{
323 for( map<string,FGInputEvent_ptr>::iterator it = handledEvents.begin(); it != handledEvents.end(); it++ )
324 (*it).second->update( dt );
325
326 report_setting_list_t::const_iterator it;
327 for (it = reportSettings.begin(); it != reportSettings.end(); ++it) {
328 if ((*it)->Test()) {
329 std::string reportData = (*it)->reportBytes(nasalModule);
330 SendFeatureReport((*it)->getReportId(), reportData);
331 }
332 }
333}
334
336{
337 string eventName = TranslateEventName( eventData );
338 if( debugEvents ) {
339 SG_LOG(SG_INPUT, SG_INFO, class_id << " " << GetUniqueName() << " has event " <<
340 eventName << " modifiers=" << eventData.modifiers << " value=" << eventData.value);
341 }
342 lastEventName->setStringValue(eventName);
343 lastEventValue->setValue(eventData.value);
344 if( handledEvents.count( eventName ) > 0 ) {
345 handledEvents[ eventName ]->fire( eventData );
346 }
347}
348
350{
351 this->name = name;
352}
353
354void FGInputDevice::SetUniqueName(const std::string &name)
355{
357}
358
359void FGInputDevice::SetSerialNumber( std::string serial )
360{
361 serialNumber = serial;
362}
363
364void FGInputDevice::SendFeatureReport(unsigned int reportId, const std::string& data)
365{
366 SG_LOG(SG_INPUT, SG_WARN, "SendFeatureReport not implemented");
367}
368
369
373
379
384
386{
387 SG_LOG(SG_INPUT, SG_DEBUG, "FGEventInput::shutdown()");
388 auto tmp = inputDevices;
389 for (auto it : tmp) {
390 RemoveDevice(it.first);
391 }
392 inputDevices.clear();
393}
394
396{
398}
399
401{
402}
403
404void FGEventInput::update( double dt )
405{
406 for (auto it : inputDevices) {
407 it.second->update(dt);
408 }
409}
410
411std::string FGEventInput::computeDeviceIndexName(FGInputDevice* dev) const
412{
413 int count = 0;
414 const auto devName = dev->GetName();
415 for (auto it : inputDevices) {
416 if (it.second->GetName() == devName) {
417 ++count;
418 }
419 }
420
421 std::ostringstream os;
422 os << devName << "_" << count;
423 return os.str();
424}
425
426unsigned FGEventInput::AddDevice( FGInputDevice * inputDevice )
427{
428 SGPropertyNode_ptr baseNode = fgGetNode( propertyRoot, true );
429 SGPropertyNode_ptr deviceNode = nullptr;
430
431 const string deviceName = inputDevice->GetName();
432 SGPropertyNode_ptr configNode = nullptr;
433
434 // if we have a serial number set, try using that to select a specfic configuration
435 if (!inputDevice->GetSerialNumber().empty()) {
436 const string nameWithSerial = deviceName + "::" + inputDevice->GetSerialNumber();
437 if (configMap.hasConfiguration(nameWithSerial)) {
438 configNode = configMap.configurationForDeviceName(nameWithSerial);
439 SG_LOG(SG_INPUT, SG_INFO, "using instance-specific configuration for device "
440 << nameWithSerial << " : " << configNode->getStringValue("source"));
441 inputDevice->SetUniqueName(nameWithSerial);
442 }
443 }
444 if (configNode == nullptr) {
445 const auto nameWithIndex = computeDeviceIndexName(inputDevice);
446 // try instanced (counted) name
447 if (configMap.hasConfiguration(nameWithIndex)) {
448 configNode = configMap.configurationForDeviceName(nameWithIndex);
449 SG_LOG(SG_INPUT, SG_INFO, "using instance-specific configuration for device "
450 << nameWithIndex << " : " << configNode->getStringValue("source"));
451 }
452 // otherwise try the unmodifed name for the device
453 else if (configMap.hasConfiguration(deviceName)) {
454 configNode = configMap.configurationForDeviceName(deviceName);
455 }
456 else {
457 SG_LOG(SG_INPUT, SG_INFO, "No configuration found for device " << deviceName);
458 delete inputDevice;
460 }
461 inputDevice->SetUniqueName(nameWithIndex);
462 }
463
464 // found - copy to /input/event/device[n]
465 // find a free index
466 unsigned int index;
467 for (index = 0; index < MAX_DEVICES; index++) {
468 if ((deviceNode = baseNode->getNode("device", index, false)) == nullptr)
469 break;
470 }
471
472 if (index == MAX_DEVICES) {
473 SG_LOG(SG_INPUT, SG_WARN, "To many event devices - ignoring " << inputDevice->GetUniqueName() );
474 delete inputDevice;
476 }
477
478 // create this node
479 deviceNode = baseNode->getNode( "device", index, true );
480
481 // and copy the properties from the configuration tree
482 copyProperties(configNode, deviceNode );
483
484 inputDevice->Configure( deviceNode );
485
486 bool ok = inputDevice->Open();
487 if (!ok) {
488 // TODO repot a better error here, to the user
489 SG_LOG(SG_INPUT, SG_ALERT, "can't open InputDevice " << inputDevice->GetUniqueName());
490 delete inputDevice;
492 }
493
494 inputDevices[deviceNode->getIndex()] = inputDevice;
495
496 SG_LOG(SG_INPUT, SG_INFO, inputDevice->class_id << "::AddDevice '" << inputDevice->GetUniqueName() << "' s/n: " << inputDevice->GetSerialNumber() );
497 return deviceNode->getIndex();
498}
499
500void FGEventInput::RemoveDevice( unsigned index )
501{
502 // not fully implemented yet
503 SGPropertyNode_ptr baseNode = fgGetNode( propertyRoot, true );
504 SGPropertyNode_ptr deviceNode = NULL;
505
506 SG_LOG(SG_INPUT, SG_DEBUG, "FGEventInput::RemoveDevice(" << index << ") ");
507 FGInputDevice *inputDevice = inputDevices[index];
508 if (inputDevice) {
509 SG_LOG(SG_INPUT, SG_DEBUG, "\tremoving (" << index << ") " << inputDevice->GetUniqueName());
510 inputDevice->Close();
511 inputDevices.erase(index);
512 delete inputDevice;
513 }
514 deviceNode = baseNode->removeChild("device", index);
515}
516
517FGReportSetting::FGReportSetting( SGPropertyNode_ptr base )
518{
519 reportId = base->getIntValue("report-id");
520 nasalFunction = base->getStringValue("nasal-function");
521
522 PropertyList watchNodes = base->getChildren( "watch" );
523 for (PropertyList::iterator it = watchNodes.begin(); it != watchNodes.end(); ++it ) {
524 std::string path = (*it)->getStringValue();
525 SGPropertyNode_ptr n = globals->get_props()->getNode(path, true);
526 n->addChangeListener(this);
527 }
528
529 dirty = true;
530}
531
533{
534 bool d = dirty;
535 dirty = false;
536 return d;
537}
538
539std::string FGReportSetting::reportBytes(const std::string& moduleName) const
540{
541 auto nas = globals->get_subsystem<FGNasalSys>();
542 if (!nas) {
543 return {};
544 }
545
546 naRef module = nas->getModule(moduleName.c_str());
547 if (naIsNil(module)) {
548 SG_LOG(SG_IO, SG_WARN, "No such Nasal module:" << moduleName);
549 return {};
550 }
551
552 naRef func = naHash_cget(module, (char*) nasalFunction.c_str());
553 if (!naIsFunc(func)) {
554 return std::string();
555 }
556
557 naRef result = nas->call(func, 0, 0, naNil());
558 if (naIsString(result)) {
559 size_t len = naStr_len(result);
560 char* bytes = naStr_data(result);
561 return std::string(bytes, len);
562 }
563
564 if (naIsVector(result)) {
565 int len = naVec_size(result);
566 std::string s;
567 for (int b=0; b < len; ++b) {
568 int num = naNumValue(naVec_get(result, b)).num;
569 s.push_back(static_cast<char>(num));
570 }
571
572 // can't access FGInputDevice here to check debugEvents flag
573#if 0
574 std::ostringstream byteString;
575 static const char* hexTable = "0123456789ABCDEF";
576
577 for (int i=0; i<s.size(); ++i) {
578 uint8_t uc = static_cast<uint8_t>(s[i]);
579 byteString << hexTable[uc >> 4];
580 byteString << hexTable[uc & 0x0f];
581 byteString << " ";
582 }
583 SG_LOG(SG_INPUT, SG_INFO, "report bytes: (" << s.size() << ") " << byteString.str());
584#endif
585
586 return s;
587 }
588
589 SG_LOG(SG_INPUT, SG_DEV_WARN, "bad return data from report setting");
590 return {};
591}
592
593void FGReportSetting::valueChanged(SGPropertyNode * node)
594{
595 dirty = true;
596}
static bool StartsWith(string &s, const char *cp)
class SGSharedPtr< FGInputEvent > FGInputEvent_ptr
SGSharedPtr< FGReportSetting > FGReportSetting_ptr
const char * hexTable
#define i(x)
void fire(SGAbstractBinding *binding, FGEventData &eventData) override
bool mirrorInterpolater
double highThreshold
virtual void fire(FGEventData &eventData)
FGAxisEvent(FGInputDevice *device, SGPropertyNode_ptr eventNode)
std::unique_ptr< SGInterpTable > interpolater
double lowThreshold
void update(double dt) override
virtual void fire(FGEventData &eventData)
FGButtonEvent(FGInputDevice *device, SGPropertyNode_ptr node)
static void read_bindings(const SGPropertyNode *base, binding_list_t *binding_list, int modifiers, const std::string &module)
void update(double dt) override
std::map< int, FGInputDevice * > inputDevices
FGDeviceConfigurationMap configMap
const char * filePath
void RemoveDevice(unsigned index)
const char * propertyRoot
void postinit() override
static const unsigned MAX_DEVICES
void shutdown() override
unsigned AddDevice(FGInputDevice *inputDevice)
virtual ~FGEventInput()
static const unsigned INVALID_DEVICE_INDEX
void init() override
SGSharedPtr< const SGCondition > condition
FGEventSetting(SGPropertyNode_ptr base)
SGPropertyNode_ptr valueNode
std::string class_id
virtual bool Open()=0
void SetName(std::string name)
void SetUniqueName(const std::string &name)
virtual void Configure(SGPropertyNode_ptr deviceNode)
SGPropertyNode_ptr deviceNode
std::string & GetSerialNumber()
std::string name
virtual void AddHandledEvent(FGInputEvent_ptr handledEvent)
SGPropertyNode_ptr lastEventValue
SGPropertyNode_ptr lastEventName
void HandleEvent(FGEventData &eventData)
report_setting_list_t reportSettings
virtual void SendFeatureReport(unsigned int reportId, const std::string &data)
const std::string GetUniqueName() const
std::map< std::string, FGInputEvent_ptr > handledEvents
virtual void update(double dt)
std::string serialNumber
std::string _uniqueName
name, but with suffix / serial appended.
virtual ~FGInputDevice()
std::string nasalModule
std::string & GetName()
virtual const char * TranslateEventName(FGEventData &eventData)=0
void SetSerialNumber(std::string serial)
virtual void Close()=0
std::string GetName() const
double lastSettingValue
virtual ~FGInputEvent()
binding_list_t bindings[KEYMOD_MAX]
FGInputDevice * device
std::string desc
FGInputEvent(FGInputDevice *device, SGPropertyNode_ptr eventNode)
virtual void update(double dt)
std::string name
static FGInputEvent * NewObject(FGInputDevice *device, SGPropertyNode_ptr node)
setting_list_t settings
virtual void fire(FGEventData &eventData)
bool createModule(const char *moduleName, const char *fileName, const char *src, int len, const SGPropertyNode *cmdarg=0, int argc=0, naRef *args=0)
naRef getModule(const std::string &moduleName) const
void fire(SGAbstractBinding *binding, FGEventData &eventData) override
FGRelAxisEvent(FGInputDevice *device, SGPropertyNode_ptr eventNode)
FGReportSetting(SGPropertyNode_ptr base)
std::string reportBytes(const std::string &moduleName) const
virtual void valueChanged(SGPropertyNode *node)
std::string nasalFunction
unsigned int reportId
@ KEYMOD_NONE
Definition fg_os.hxx:24
@ KEYMOD_RELEASED
Definition fg_os.hxx:25
FGGlobals * globals
Definition globals.cxx:142
naCFunction func
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27