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