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.
25 #include "FGJoystickInput.hxx"
27 #include <simgear/props/props_io.hxx>
28 #include "FGDeviceConfigurationMap.hxx"
29 #include <Main/fg_props.hxx>
30 #include <Scripting/NasalSys.hxx>
32 using simgear::PropertyList;
34 FGJoystickInput::axis::axis ()
35 : last_value(9999999),
44 FGJoystickInput::axis::~axis ()
48 FGJoystickInput::joystick::joystick ()
59 FGJoystickInput::joystick::~joystick ()
61 // delete js? no, since js == this - and we're in the destructor already.
73 FGJoystickInput::FGJoystickInput()
77 FGJoystickInput::~FGJoystickInput()
82 void FGJoystickInput::_remove(bool all)
84 SGPropertyNode * js_nodes = fgGetNode("/input/joysticks", true);
86 for (int i = 0; i < MAX_JOYSTICKS; i++)
88 // do not remove predefined joysticks info on reinit
89 if ((all)||(!bindings[i].predefined))
90 js_nodes->removeChild("js", i, false);
92 delete bindings[i].js;
93 bindings[i].js = NULL;
97 void FGJoystickInput::init()
100 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick bindings");
101 SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks", true);
103 FGDeviceConfigurationMap configMap("Input/Joysticks", js_nodes, "js-named");
105 for (int i = 0; i < MAX_JOYSTICKS; i++) {
106 jsJoystick * js = new jsJoystick(i);
109 if (js->notWorking()) {
110 SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found");
114 const char * name = js->getName();
115 SGPropertyNode_ptr js_node = js_nodes->getChild("js", i);
118 SG_LOG(SG_INPUT, SG_INFO, "Using existing bindings for joystick " << i);
121 bindings[i].predefined = false;
122 SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
123 SGPropertyNode_ptr named;
125 if ((named = configMap[name])) {
126 string source = named->getStringValue("source", "user defined");
127 SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
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 << '"');
135 SG_LOG(SG_INPUT, SG_WARN, "No joystick configuration file with <name>" << name << "</name> entry found!");
138 js_node = js_nodes->getChild("js", i, true);
139 copyProperties(named, js_node);
140 js_node->setStringValue("id", name);
145 void FGJoystickInput::reinit() {
146 SG_LOG(SG_INPUT, SG_DEBUG, "Re-Initializing joystick bindings");
148 FGJoystickInput::init();
149 FGJoystickInput::postinit();
152 void FGJoystickInput::postinit()
154 FGNasalSys *nasalsys = (FGNasalSys *)globals->get_subsystem("nasal");
155 SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks");
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())
165 joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
166 unsigned int nbuttons = jsCaps.wNumButtons;
167 if (nbuttons > MAX_JOYSTICK_BUTTONS) nbuttons = MAX_JOYSTICK_BUTTONS;
169 unsigned int nbuttons = MAX_JOYSTICK_BUTTONS;
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;
177 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing joystick " << i);
179 // Set up range arrays
180 float minRange[MAX_JOYSTICK_AXES];
181 float maxRange[MAX_JOYSTICK_AXES];
182 float center[MAX_JOYSTICK_AXES];
184 // Initialize with default values
185 js->getMinRange(minRange);
186 js->getMaxRange(maxRange);
187 js->getCenter(center);
189 // Allocate axes and buttons
190 bindings[i].axes = new axis[naxes];
191 bindings[i].buttons = new FGButton[nbuttons];
194 // Initialize nasal groups.
198 string module = str.str();
199 nasalsys->createModule(module.c_str(), module.c_str(), "", 0);
201 PropertyList nasal = js_node->getChildren("nasal");
203 for (j = 0; j < nasal.size(); j++) {
204 nasal[j]->setStringValue("module", module.c_str());
205 nasalsys->handleCommand(nasal[j]);
209 // Initialize the axes.
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();
218 n_axis = num_node->getIntValue(TGT_PLATFORM, -1);
220 // Silently ignore platforms that are not specified within the
221 // <number></number> section
226 if (n_axis >= naxes) {
227 SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for axis " << n_axis);
230 axis &a = bindings[i].axes[n_axis];
232 js->setDeadBand(n_axis, axis_node->getDoubleValue("dead-band", 0.0));
234 a.tolerance = axis_node->getDoubleValue("tolerance", 0.002);
235 minRange[n_axis] = axis_node->getDoubleValue("min-range", minRange[n_axis]);
236 maxRange[n_axis] = axis_node->getDoubleValue("max-range", maxRange[n_axis]);
237 center[n_axis] = axis_node->getDoubleValue("center", center[n_axis]);
239 read_bindings(axis_node, a.bindings, KEYMOD_NONE, module );
241 // Initialize the virtual axis buttons.
242 a.low.init(axis_node->getChild("low"), "low", module );
243 a.low_threshold = axis_node->getDoubleValue("low-threshold", -0.9);
245 a.high.init(axis_node->getChild("high"), "high", module );
246 a.high_threshold = axis_node->getDoubleValue("high-threshold", 0.9);
247 a.interval_sec = axis_node->getDoubleValue("interval-sec",0.0);
252 // Initialize the buttons.
254 PropertyList buttons = js_node->getChildren("button");
256 for (j = 0; j < buttons.size() && j < nbuttons; j++) {
257 const SGPropertyNode * button_node = buttons[j];
258 const SGPropertyNode * num_node = button_node->getChild("number");
259 size_t n_but = button_node->getIndex();
261 n_but = num_node->getIntValue(TGT_PLATFORM,n_but);
264 if (n_but >= nbuttons) {
265 SG_LOG(SG_INPUT, SG_DEBUG, "Dropping bindings for button " << n_but);
269 sprintf(buf, "%u", (unsigned)n_but);
270 SG_LOG(SG_INPUT, SG_DEBUG, "Initializing button " << n_but);
271 bindings[i].buttons[n_but].init(button_node, buf, module );
273 // get interval-sec property
274 FGButton &b = bindings[i].buttons[n_but];
275 if (button_node != 0) {
276 b.interval_sec = button_node->getDoubleValue("interval-sec",0.0);
281 js->setMinRange(minRange);
282 js->setMaxRange(maxRange);
283 js->setCenter(center);
287 void FGJoystickInput::update( double dt )
289 float axis_values[MAX_JOYSTICK_AXES];
290 int modifiers = fgGetKeyModifiers();
293 for (int i = 0; i < MAX_JOYSTICKS; i++) {
295 jsJoystick * js = bindings[i].js;
296 if (js == 0 || js->notWorking())
299 js->read(&buttons, axis_values);
300 if (js->notWorking()) // If js is disconnected
303 // Fire bindings for the axes.
304 for (int j = 0; j < bindings[i].naxes; j++) {
305 axis &a = bindings[i].axes[j];
307 // Do nothing if the axis position
308 // is unchanged; only a change in
309 // position fires the bindings.
310 // But only if there are bindings
311 if (fabs(axis_values[j] - a.last_value) > a.tolerance
312 && a.bindings[KEYMOD_NONE].size() > 0 ) {
313 a.last_value = axis_values[j];
314 for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++)
315 a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]);
318 // do we have to emulate axis buttons?
320 if(a.last_dt >= a.interval_sec) {
321 if (a.low.bindings[modifiers].size())
322 bindings[i].axes[j].low.update( modifiers, axis_values[j] < a.low_threshold );
324 if (a.high.bindings[modifiers].size())
325 bindings[i].axes[j].high.update( modifiers, axis_values[j] > a.high_threshold );
327 a.last_dt -= a.interval_sec;
331 // Fire bindings for the buttons.
332 for (int j = 0; j < bindings[i].nbuttons; j++) {
333 FGButton &b = bindings[i].buttons[j];
335 if(b.last_dt >= b.interval_sec) {
336 bindings[i].buttons[j].update( modifiers, (buttons & (1u << j)) > 0 );
337 b.last_dt -= b.interval_sec;