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