]> git.mxchange.org Git - flightgear.git/blob - src/Input/FGJoystickInput.cxx
TACAN improvements.
[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     last_dt(0)
46 {
47 }
48
49 FGJoystickInput::axis::~axis ()
50 {
51 }
52
53 FGJoystickInput::joystick::joystick ()
54   : jsnum(0),
55     naxes(0),
56     nbuttons(0),
57     axes(0),
58     buttons(0),
59     predefined(true)
60 {
61 }
62
63 void FGJoystickInput::joystick::clearAxesAndButtons()
64 {
65     delete[] axes;
66     delete[] buttons;
67     axes = NULL;
68     buttons = NULL;
69     naxes = 0;
70     nbuttons = 0;
71 }
72
73 FGJoystickInput::joystick::~joystick ()
74 {
75   jsnum = 0;
76   clearAxesAndButtons();
77 }
78
79
80 FGJoystickInput::FGJoystickInput()
81 {
82 }
83
84 FGJoystickInput::~FGJoystickInput()
85 {
86     _remove(true);
87 }
88
89 void FGJoystickInput::_remove(bool all)
90 {
91     SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
92
93     for (int i = 0; i < MAX_JOYSTICKS; i++)
94     {
95         joystick* joy = &joysticks[i];
96         // do not remove predefined joysticks info on reinit
97         if (all || (!joy->predefined))
98             js_nodes->removeChild("js", i, false);
99         
100         joy->plibJS.reset();
101         joy->clearAxesAndButtons();
102     }
103 }
104
105 void FGJoystickInput::init()
106 {
107   jsInit();
108   SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
109   SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks", true);
110   status_node = fgGetNode("/devices/status/joysticks", true);
111
112   FGDeviceConfigurationMap configMap("Input/Joysticks",js_nodes, "js-named");
113
114   for (int i = 0; i < MAX_JOYSTICKS; i++) {
115     jsJoystick * js = new jsJoystick(i);
116     joysticks[i].plibJS.reset(js);
117     
118     if (js->notWorking()) {
119       SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
120       continue;
121     }
122
123     const char * name = js->getName();
124     SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
125
126     if (js_node) {
127       SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
128
129     } else {
130       joysticks[i].predefined = false;
131       SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
132       SGPropertyNode_ptr named;
133
134       if (configMap.hasConfiguration(name)) {
135         named = configMap.configurationForDeviceName(name);
136         std::string source = named->getStringValue("source", "user defined");
137         SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
138
139       } else if ((named = configMap.configurationForDeviceName("default"))) {
140         std::string source = named->getStringValue("source", "user defined");
141         SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
142             << "\"\nUsing default: \"" << source << '"');
143
144       } else {
145         SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
146       }
147
148       js_node = js_nodes->getChild("js", i, true);
149       copyProperties(named, js_node);
150       js_node->setStringValue("id", name);
151     }
152   }
153 }
154
155 void FGJoystickInput::reinit() {
156   SG_LOG(SG_INPUT, SG_DEBUG, "Re-Initializing joystick bindings");
157   _remove(false);
158   FGJoystickInput::init();
159   FGJoystickInput::postinit();
160 }
161
162 void FGJoystickInput::postinit()
163 {
164   FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
165   SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
166
167   for (int i = 0; i < MAX_JOYSTICKS; i++) {
168     SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
169     jsJoystick *js = joysticks[i].plibJS.get();
170     if (!js_node || js->notWorking())
171       continue;
172
173 #ifdef WIN32
174     JOYCAPS jsCaps ;
175     joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
176     unsigned int nbuttons = jsCaps.wNumButtons;
177     if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
178 #else
179     unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
180 #endif
181
182     int naxes = js->getNumAxes();
183     if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
184     joysticks[i].naxes = naxes;
185     joysticks[i].nbuttons = nbuttons;
186
187     SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
188
189                                 // Set up range arrays
190     float minRange[MAX_JOYSTICK_AXES];
191     float maxRange[MAX_JOYSTICK_AXES];
192     float center[MAX_JOYSTICK_AXES];
193
194                                 // Initialize with default values
195     js->getMinRange(minRange);
196     js->getMaxRange(maxRange);
197     js->getCenter(center);
198
199                                 // Allocate axes and buttons
200     joysticks[i].axes = new axis[naxes];
201     joysticks[i].buttons = new FGButton[nbuttons];
202
203     //
204     // Initialize nasal groups.
205     //
206     std::ostringstream str;
207     str << "__js" << i;
208     std::string module = str.str();
209     nasalsys->createModule(module.c_str(), module.c_str(), "", 0);
210
211     PropertyList nasal = js_node->getChildren("nasal");
212     unsigned int j;
213     for (j = 0; j < nasal.size(); j++) {
214       nasal[j]->setStringValue("module", module.c_str());
215       nasalsys->handleCommand(nasal[j]);
216     }
217
218     //
219     // Initialize the axes.
220     //
221     PropertyList axes = js_node->getChildren("axis");
222     size_t nb_axes = axes.size();
223     for (j = 0; j < nb_axes; j++ ) {
224       const SGPropertyNode * axis_node = axes[j];
225       const SGPropertyNode * num_node = axis_node->getChild("number");
226       int n_axis = axis_node->getIndex();
227       if (num_node != 0) {
228           n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
229
230         #ifdef SG_MAC
231           // Mac falls back to Unix by default - avoids specifying many
232           // duplicate <mac> entries in joystick config files
233           if (n_axis < 0) {
234               n_axis = num_node->getIntValue("unix", -1);
235           }
236         #endif
237           
238           // Silently ignore platforms that are not specified within the
239           // <number></number> section
240           if (n_axis < 0) {
241               continue;
242           }
243       }
244
245       if (n_axis >= naxes) {
246           SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
247           continue;
248       }
249       axis &a = joysticks[i].axes[n_axis];
250
251       js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
252
253       a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
254       minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
255       maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
256       center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
257
258       read_bindings(axis_node, a.bindings, KEYMOD_NONE, module );
259
260       // Initialize the virtual axis buttons.
261       a.low.init(axis_node->getChild("low"), "low", module );
262       a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
263
264       a.high.init(axis_node->getChild("high"), "high", module );
265       a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
266       a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
267       a.last_dt = 0.0;
268     }
269
270     //
271     // Initialize the buttons.
272     //
273     PropertyList buttons = js_node->getChildren("button");
274     BOOST_FOREACH( SGPropertyNode * button_node, buttons ) {
275       size_t n_but = button_node->getIndex();
276
277       const SGPropertyNode * num_node = button_node->getChild("number");
278       if (NULL != num_node)
279           n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
280
281       if (n_but >= nbuttons) {
282           SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
283           continue;
284       }
285
286       std::ostringstream buf;
287       buf << (unsigned)n_but;
288
289       SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << buf.str());
290       FGButton &b = joysticks[i].buttons[n_but];
291       b.init(button_node, buf.str(), module );
292       // get interval-sec property
293       b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
294       b.last_dt = 0.0;
295     }
296
297     js->setMinRange(minRange);
298     js->setMaxRange(maxRange);
299     js->setCenter(center);
300   }
301 }
302
303 void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, double dt)
304 {
305   float axis_values[MAX_JOYSTICK_AXES];
306   int modifiers = fgGetKeyModifiers();
307   int buttons;
308   
309   jsJoystick * js = joy->plibJS.get();
310   if (js == 0 || js->notWorking())
311     return;
312   
313   js->read(&buttons, axis_values);
314   if (js->notWorking()) // If js is disconnected
315     return;
316   
317   // Update device status
318   SGPropertyNode_ptr status = status_node->getChild("joystick", index, true);
319   for (int j = 0; j < MAX_JOYSTICK_AXES; j++) {
320     status->getChild("axis", j, true)->setFloatValue(axis_values[j]);
321   }
322   
323   for (int j = 0; j < MAX_JOYSTICK_BUTTONS; j++) {
324     status->getChild("button", j, true)->setBoolValue((buttons & (1u << j)) > 0 );
325   }
326   
327   // Fire bindings for the axes.
328   for (int j = 0; j < joy->naxes; j++) {
329     axis &a = joy->axes[j];
330     
331     // Do nothing if the axis position
332     // is unchanged; only a change in
333     // position fires the bindings.
334     // But only if there are bindings
335     if (fabs(axis_values[j] - a.last_value) > a.tolerance
336         && a.bindings[KEYMOD_NONE].size() > 0 ) {
337       a.last_value = axis_values[j];
338       for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
339         a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
340     }
341     
342     // do we have to emulate axis buttons?
343     a.last_dt += dt;
344     if(a.last_dt >= a.interval_sec) {
345       if (a.low.bindings[modifiers].size())
346         joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
347       
348       if (a.high.bindings[modifiers].size())
349         joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
350       
351       a.last_dt -= a.interval_sec;
352     }
353   } // of axes iteration
354   
355   // Fire bindings for the buttons.
356   for (int j = 0; j < joy->nbuttons; j++) {
357     FGButton &b = joy->buttons[j];
358     b.last_dt += dt;
359     if(b.last_dt >= b.interval_sec) {
360       joy->buttons[j].update( modifiers, (buttons & (1u << j)) > 0 );
361       b.last_dt -= b.interval_sec;
362     }
363   } // of butotns iterations
364
365 }
366
367 void FGJoystickInput::update( double dt )
368 {
369   for (int i = 0; i < MAX_JOYSTICKS; i++) {
370     updateJoystick(i, &joysticks[i], dt);
371   }
372 }
373