1 // FGJoystickInput.cxx -- handle user input from joystick devices
3 // Written by Torsten Dreyer, started August 2009
4 // Based on work from David Megginson, started May 2001.
6 // Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
7 // Copyright (C) 2001 David Megginson, david@megginson.com
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.
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.
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.
29 #include "FGJoystickInput.hxx"
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>
37 using simgear::PropertyList;
39 FGJoystickInput::axis::axis ()
40 : last_value(9999999),
51 FGJoystickInput::axis::~axis ()
55 FGJoystickInput::joystick::joystick ()
65 void FGJoystickInput::joystick::clearAxesAndButtons()
75 FGJoystickInput::joystick::~joystick ()
78 clearAxesAndButtons();
82 FGJoystickInput::FGJoystickInput()
86 FGJoystickInput::~FGJoystickInput()
91 void FGJoystickInput::_remove(bool all)
93 SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
95 for (int i = 0; i < MAX_JOYSTICKS; i++)
97 joystick* joy = &joysticks[i];
98 // do not remove predefined joysticks info on reinit
99 if (all || (!joy->predefined))
100 js_nodes->removeChild("js", i);
103 joy->clearAxesAndButtons();
107 void FGJoystickInput::init()
110 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
111 SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks", true);
112 status_node = fgGetNode("/devices/status/joysticks", true);
114 FGDeviceConfigurationMap configMap("Input/Joysticks",js_nodes, "js-named");
116 for (int i = 0; i < MAX_JOYSTICKS; i++) {
117 jsJoystick * js = new jsJoystick(i);
118 joysticks[i].plibJS.reset(js);
120 if (js->notWorking()) {
121 SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
125 const char * name = js->getName();
126 SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
129 SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
132 joysticks[i].predefined = false;
133 SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
134 SGPropertyNode_ptr named;
136 if (configMap.hasConfiguration(name)) {
137 named = configMap.configurationForDeviceName(name);
138 std::string source = named->getStringValue("source", "user defined");
139 SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
141 } else if ((named = configMap.configurationForDeviceName("default"))) {
142 std::string source = named->getStringValue("source", "user defined");
143 SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
144 << "\"\nUsing default: \"" << source << '"');
147 SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
150 js_node = js_nodes->getChild("js", i, true);
151 copyProperties(named, js_node);
152 js_node->setStringValue("id", name);
157 void FGJoystickInput::reinit() {
158 SG_LOG(SG_INPUT, SG_DEBUG, "Re-Initializing joystick bindings");
160 FGJoystickInput::init();
161 FGJoystickInput::postinit();
164 void FGJoystickInput::postinit()
166 FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
167 SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
169 for (int i = 0; i < MAX_JOYSTICKS; i++) {
170 SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
171 jsJoystick *js = joysticks[i].plibJS.get();
172 if (!js_node || js->notWorking())
177 joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
178 unsigned int nbuttons = jsCaps.wNumButtons;
179 if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
181 unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
184 int naxes = js->getNumAxes();
185 if (naxes > MAX_JOYSTICK_AXES) naxes = MAX_JOYSTICK_AXES;
186 joysticks[i].naxes = naxes;
187 joysticks[i].nbuttons = nbuttons;
189 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
191 // Set up range arrays
192 float minRange[MAX_JOYSTICK_AXES];
193 float maxRange[MAX_JOYSTICK_AXES];
194 float center[MAX_JOYSTICK_AXES];
196 // Initialize with default values
197 js->getMinRange(minRange);
198 js->getMaxRange(maxRange);
199 js->getCenter(center);
201 // Allocate axes and buttons
202 joysticks[i].axes = new axis[naxes];
203 joysticks[i].buttons = new FGButton[nbuttons];
206 // Initialize nasal groups.
208 std::ostringstream str;
210 std::string module = str.str();
211 nasalsys->createModule(module.c_str(), module.c_str(), "", 0);
213 PropertyList nasal = js_node->getChildren("nasal");
215 for (j = 0; j < nasal.size(); j++) {
216 nasal[j]->setStringValue("module", module.c_str());
217 nasalsys->handleCommand(nasal[j]);
221 // Initialize the axes.
223 PropertyList axes = js_node->getChildren("axis");
224 size_t nb_axes = axes.size();
225 for (j = 0; j < nb_axes; j++ ) {
226 const SGPropertyNode * axis_node = axes[j];
227 const SGPropertyNode * num_node = axis_node->getChild("number");
228 int n_axis = axis_node->getIndex();
230 n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
233 // Mac falls back to Unix by default - avoids specifying many
234 // duplicate <mac> entries in joystick config files
236 n_axis = num_node->getIntValue("unix", -1);
240 // Silently ignore platforms that are not specified within the
241 // <number></number> section
247 if (n_axis >= naxes) {
248 SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
251 axis &a = joysticks[i].axes[n_axis];
253 js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
255 a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
256 minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
257 maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
258 center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
260 read_bindings(axis_node, a.bindings, KEYMOD_NONE, module );
262 // Initialize the virtual axis buttons.
263 a.low.init(axis_node->getChild("low"), "low", module );
264 a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
266 a.high.init(axis_node->getChild("high"), "high", module );
267 a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
268 a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
269 a.delay_sec = axis_node->getDoubleValue("delay-sec",0.0);
270 a.release_delay_sec = axis_node->getDoubleValue("release-delay-sec",0.0);
275 // Initialize the buttons.
277 PropertyList buttons = js_node->getChildren("button");
278 BOOST_FOREACH( SGPropertyNode * button_node, buttons ) {
279 size_t n_but = button_node->getIndex();
281 const SGPropertyNode * num_node = button_node->getChild("number");
282 if (NULL != num_node)
283 n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
285 if (n_but >= nbuttons) {
286 SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
290 std::ostringstream buf;
291 buf << (unsigned)n_but;
293 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << buf.str());
294 FGButton &b = joysticks[i].buttons[n_but];
295 b.init(button_node, buf.str(), module );
296 // get interval-sec property
297 b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
298 b.delay_sec = button_node->getDoubleValue("delay-sec",0.0);
299 b.release_delay_sec = button_node->getDoubleValue("release-delay-sec",0.0);
303 js->setMinRange(minRange);
304 js->setMaxRange(maxRange);
305 js->setCenter(center);
309 void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, double dt)
311 float axis_values[MAX_JOYSTICK_AXES];
312 int modifiers = fgGetKeyModifiers();
314 bool pressed, last_state;
317 jsJoystick * js = joy->plibJS.get();
318 if (js == 0 || js->notWorking())
321 js->read(&buttons, axis_values);
322 if (js->notWorking()) // If js is disconnected
325 // Update device status
326 SGPropertyNode_ptr status = status_node->getChild("joystick", index, true);
327 for (int j = 0; j < MAX_JOYSTICK_AXES; j++) {
328 status->getChild("axis", j, true)->setFloatValue(axis_values[j]);
331 for (int j = 0; j < MAX_JOYSTICK_BUTTONS; j++) {
332 status->getChild("button", j, true)->setBoolValue((buttons & (1u << j)) > 0 );
335 // Fire bindings for the axes.
336 for (int j = 0; j < joy->naxes; j++) {
337 axis &a = joy->axes[j];
339 // Do nothing if the axis position
340 // is unchanged; only a change in
341 // position fires the bindings.
342 // But only if there are bindings
343 if (fabs(axis_values[j] - a.last_value) > a.tolerance
344 && a.bindings[KEYMOD_NONE].size() > 0 ) {
345 a.last_value = axis_values[j];
346 for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
347 a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
350 // do we have to emulate axis buttons?
351 last_state = joy->axes[j].low.last_state || joy->axes[j].high.last_state;
352 pressed = axis_values[j] < a.low_threshold || axis_values[j] > a.high_threshold;
353 delay = (pressed ? last_state ? a.interval_sec : a.delay_sec : a.release_delay_sec );
354 if(pressed || last_state) a.last_dt += dt;
356 if(a.last_dt >= delay) {
357 if (a.low.bindings[modifiers].size())
358 joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
360 if (a.high.bindings[modifiers].size())
361 joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
365 } // of axes iteration
367 // Fire bindings for the buttons.
368 for (int j = 0; j < joy->nbuttons; j++) {
369 FGButton &b = joy->buttons[j];
370 pressed = (buttons & (1u << j)) > 0;
371 last_state = joy->buttons[j].last_state;
372 delay = (pressed ? last_state ? b.interval_sec : b.delay_sec : b.release_delay_sec );
373 if(pressed || last_state) b.last_dt += dt;
375 if(b.last_dt >= delay) {
376 joy->buttons[j].update( modifiers, pressed );
379 } // of butotns iterations
383 void FGJoystickInput::update( double dt )
385 for (int i = 0; i < MAX_JOYSTICKS; i++) {
386 updateJoystick(i, &joysticks[i], dt);