]> git.mxchange.org Git - flightgear.git/blob - src/Input/FGJoystickInput.cxx
Allow using the system version of flite and the HTS engine
[flightgear.git] / src / Input / FGJoystickInput.cxx
1 // FGJoystickInput.cxx -- handle user input from joystick devices
2 //
3 // Written by Torsten Dreyer, started August 2009
4 // Based on work from David Megginson, started May 2001.
5 //
6 // Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
7 // Copyright (C) 2001 David Megginson, david@megginson.com
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 //
23 // $Id$
24
25 #ifdef HAVE_CONFIG_H
26 #  include "config.h"
27 #endif
28
29 #include "FGJoystickInput.hxx"
30
31 #include <simgear/props/props_io.hxx>
32 #include "FGDeviceConfigurationMap.hxx"
33 #include <Main/fg_props.hxx>
34 #include <Scripting/NasalSys.hxx>
35 #include <boost/foreach.hpp>
36
37 using simgear::PropertyList;
38
39 FGJoystickInput::axis::axis ()
40   : last_value(9999999),
41     tolerance(0.002),
42     low_threshold(-0.9),
43     high_threshold(0.9),
44     interval_sec(0),
45     delay_sec(0),
46     release_delay_sec(0),
47     last_dt(0)
48 {
49 }
50
51 FGJoystickInput::axis::~axis ()
52 {
53 }
54
55 FGJoystickInput::joystick::joystick ()
56   : jsnum(0),
57     naxes(0),
58     nbuttons(0),
59     axes(0),
60     buttons(0),
61     predefined(true)
62 {
63 }
64
65 void FGJoystickInput::joystick::clearAxesAndButtons()
66 {
67     delete[] axes;
68     delete[] buttons;
69     axes = NULL;
70     buttons = NULL;
71     naxes = 0;
72     nbuttons = 0;
73 }
74
75 FGJoystickInput::joystick::~joystick ()
76 {
77   jsnum = 0;
78   clearAxesAndButtons();
79 }
80
81
82 FGJoystickInput::FGJoystickInput()
83 {
84 }
85
86 FGJoystickInput::~FGJoystickInput()
87 {
88     _remove(true);
89 }
90
91 void FGJoystickInput::_remove(bool all)
92 {
93     SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
94
95     for (int i = 0; i < MAX_JOYSTICKS; i++)
96     {
97         joystick* joy = &joysticks[i];
98         // do not remove predefined joysticks info on reinit
99         if (all || (!joy->predefined))
100             js_nodes->removeChild("js", i);
101         
102         joy->plibJS.reset();
103         joy->clearAxesAndButtons();
104     }
105 }
106
107 void FGJoystickInput::init()
108 {
109   jsInit();
110   SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
111   SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks", true);
112   status_node = fgGetNode("/devices/status/joysticks", true);
113
114   FGDeviceConfigurationMap configMap("Input/Joysticks",js_nodes, "js-named");
115
116   for (int i = 0; i < MAX_JOYSTICKS; i++) {
117     jsJoystick * js = new jsJoystick(i);
118     joysticks[i].plibJS.reset(js);
119     
120     if (js->notWorking()) {
121       SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
122       continue;
123     }
124
125     const char * name = js->getName();
126     SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
127
128     if (js_node) {
129       SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
130
131     } else {
132       joysticks[i].predefined = false;
133       SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
134       SGPropertyNode_ptr named;
135
136       if (configMap.hasConfiguration(name)) {
137         named = configMap.configurationForDeviceName(name);
138         std::string source = named->getStringValue("source", "user defined");
139         SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
140
141       } else if ((named = configMap.configurationForDeviceName("default"))) {
142         std::string source = named->getStringValue("source", "user defined");
143         SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
144             << "\"\nUsing default: \"" << source << '"');
145
146       } else {
147         SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
148       }
149
150       js_node = js_nodes->getChild("js", i, true);
151       copyProperties(named, js_node);
152       js_node->setStringValue("id", name);
153     }
154   }
155 }
156
157 void FGJoystickInput::reinit() {
158   SG_LOG(SG_INPUT, SG_DEBUG, "Re-Initializing joystick bindings");
159   _remove(false);
160   FGJoystickInput::init();
161   FGJoystickInput::postinit();
162 }
163
164 void FGJoystickInput::postinit()
165 {
166   FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
167   SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
168
169   for (int i = 0; i < MAX_JOYSTICKS; i++) {
170     SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
171     jsJoystick *js = joysticks[i].plibJS.get();
172     if (!js_node || js->notWorking())
173       continue;
174
175 #ifdef WIN32
176     JOYCAPS jsCaps ;
177     joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
178     unsigned int nbuttons = jsCaps.wNumButtons;
179     if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
180 #else
181     unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
182 #endif
183
184     int naxes = js->getNumAxes();
185     if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
186     joysticks[i].naxes = naxes;
187     joysticks[i].nbuttons = nbuttons;
188
189     SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
190
191                                 // Set up range arrays
192     float minRange[MAX_JOYSTICK_AXES];
193     float maxRange[MAX_JOYSTICK_AXES];
194     float center[MAX_JOYSTICK_AXES];
195
196                                 // Initialize with default values
197     js->getMinRange(minRange);
198     js->getMaxRange(maxRange);
199     js->getCenter(center);
200
201                                 // Allocate axes and buttons
202     joysticks[i].axes = new axis[naxes];
203     joysticks[i].buttons = new FGButton[nbuttons];
204
205     //
206     // Initialize nasal groups.
207     //
208     std::ostringstream str;
209     str << "__js" << i;
210     std::string module = str.str();
211     nasalsys->createModule(module.c_str(), module.c_str(), "", 0);
212
213     PropertyList nasal = js_node->getChildren("nasal");
214     unsigned int j;
215     for (j = 0; j < nasal.size(); j++) {
216       nasal[j]->setStringValue("module", module.c_str());
217       nasalsys->handleCommand(nasal[j]);
218     }
219
220     //
221     // Initialize the axes.
222     //
223     PropertyList axes = js_node->getChildren("axis");
224     size_t nb_axes = axes.size();
225     for (j = 0; j < nb_axes; j++ ) {
226       const SGPropertyNode * axis_node = axes[j];
227       const SGPropertyNode * num_node = axis_node->getChild("number");
228       int n_axis = axis_node->getIndex();
229       if (num_node != 0) {
230           n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
231
232         #ifdef SG_MAC
233           // Mac falls back to Unix by default - avoids specifying many
234           // duplicate <mac> entries in joystick config files
235           if (n_axis < 0) {
236               n_axis = num_node->getIntValue("unix", -1);
237           }
238         #endif
239           
240           // Silently ignore platforms that are not specified within the
241           // <number></number> section
242           if (n_axis < 0) {
243               continue;
244           }
245       }
246
247       if (n_axis >= naxes) {
248           SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
249           continue;
250       }
251       axis &a = joysticks[i].axes[n_axis];
252
253       js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
254
255       a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
256       minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
257       maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
258       center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
259
260       read_bindings(axis_node, a.bindings, KEYMOD_NONE, module );
261
262       // Initialize the virtual axis buttons.
263       a.low.init(axis_node->getChild("low"), "low", module );
264       a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
265
266       a.high.init(axis_node->getChild("high"), "high", module );
267       a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
268       a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
269       a.delay_sec = axis_node->getDoubleValue("delay-sec",0.0);
270       a.release_delay_sec = axis_node->getDoubleValue("release-delay-sec",0.0);
271       a.last_dt = 0.0;
272     }
273
274     //
275     // Initialize the buttons.
276     //
277     PropertyList buttons = js_node->getChildren("button");
278     BOOST_FOREACH( SGPropertyNode * button_node, buttons ) {
279       size_t n_but = button_node->getIndex();
280
281       const SGPropertyNode * num_node = button_node->getChild("number");
282       if (NULL != num_node)
283           n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
284
285       if (n_but >= nbuttons) {
286           SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
287           continue;
288       }
289
290       std::ostringstream buf;
291       buf << (unsigned)n_but;
292
293       SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << buf.str());
294       FGButton &b = joysticks[i].buttons[n_but];
295       b.init(button_node, buf.str(), module );
296       // get interval-sec property
297       b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
298       b.delay_sec = button_node->getDoubleValue("delay-sec",0.0);
299       b.release_delay_sec = button_node->getDoubleValue("release-delay-sec",0.0);
300       b.last_dt = 0.0;
301     }
302
303     js->setMinRange(minRange);
304     js->setMaxRange(maxRange);
305     js->setCenter(center);
306   }
307 }
308
309 void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, double dt)
310 {
311   float axis_values[MAX_JOYSTICK_AXES];
312   int modifiers = fgGetKeyModifiers();
313   int buttons;
314   bool pressed, last_state;
315   float delay;
316   
317   jsJoystick * js = joy->plibJS.get();
318   if (js == 0 || js->notWorking())
319     return;
320   
321   js->read(&buttons, axis_values);
322   if (js->notWorking()) // If js is disconnected
323     return;
324   
325   // Update device status
326   SGPropertyNode_ptr status = status_node->getChild("joystick", index, true);
327   for (int j = 0; j < MAX_JOYSTICK_AXES; j++) {
328     status->getChild("axis", j, true)->setFloatValue(axis_values[j]);
329   }
330   
331   for (int j = 0; j < MAX_JOYSTICK_BUTTONS; j++) {
332     status->getChild("button", j, true)->setBoolValue((buttons & (1u << j)) > 0 );
333   }
334   
335   // Fire bindings for the axes.
336   for (int j = 0; j < joy->naxes; j++) {
337     axis &a = joy->axes[j];
338     
339     // Do nothing if the axis position
340     // is unchanged; only a change in
341     // position fires the bindings.
342     // But only if there are bindings
343     if (fabs(axis_values[j] - a.last_value) > a.tolerance
344         && a.bindings[KEYMOD_NONE].size() > 0 ) {
345       a.last_value = axis_values[j];
346       for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
347         a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
348     }
349     
350     // do we have to emulate axis buttons?
351     last_state = joy->axes[j].low.last_state || joy->axes[j].high.last_state;
352     pressed = axis_values[j] < a.low_threshold || axis_values[j] > a.high_threshold;
353     delay = (pressed ? last_state ? a.interval_sec : a.delay_sec : a.release_delay_sec );
354     if(pressed || last_state) a.last_dt += dt;
355     else a.last_dt = 0;
356     if(a.last_dt >= delay) {
357       if (a.low.bindings[modifiers].size())
358         joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
359       
360       if (a.high.bindings[modifiers].size())
361         joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
362       
363       a.last_dt -= delay;
364     }
365   } // of axes iteration
366   
367   // Fire bindings for the buttons.
368   for (int j = 0; j < joy->nbuttons; j++) {
369     FGButton &b = joy->buttons[j];
370     pressed = (buttons & (1u << j)) > 0;
371     last_state = joy->buttons[j].last_state;
372     delay = (pressed ? last_state ? b.interval_sec : b.delay_sec : b.release_delay_sec );
373     if(pressed || last_state) b.last_dt += dt;
374     else b.last_dt = 0;
375     if(b.last_dt >= delay) {
376       joy->buttons[j].update( modifiers, pressed );
377       b.last_dt -= delay;
378     }
379   } // of butotns iterations
380
381 }
382
383 void FGJoystickInput::update( double dt )
384 {
385   for (int i = 0; i < MAX_JOYSTICKS; i++) {
386     updateJoystick(i, &joysticks[i], dt);
387   }
388 }
389