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