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