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