1 // joystick.cxx -- joystick support
3 // Original module written by Curtis Olson, started October 1998.
4 // Completely rewritten by David Megginson, July 2000.
6 // Copyright (C) 1998 - 2000 Curtis L. Olson - curt@flightgear.org
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
37 #include <simgear/misc/props.hxx>
38 #include <simgear/debug/logstream.hxx>
40 #include "joystick.hxx"
45 static const int MAX_JOYSTICKS = 2;
47 static const int MAX_JOYSTICKS = 10;
49 static const int MAX_AXES = _JS_MAX_AXES;
50 static const int MAX_BUTTONS = 10;
54 * Property names for joysticks and axes.
56 static const char * jsNames[] = {
57 "js0", "js1", "js2", "js3", "js4",
58 "js5", "js6", "js7", "js8", "js9"
60 static const char * axisNames[] = {
61 "axis0", "axis1", "axis2", "axis3", "axis4",
62 "axis5", "axis6", "axis7", "axis8", "axis9"
64 static const char * buttonNames[] = {
65 "button0", "button1", "button2", "button3", "button4",
66 "button5", "button6", "button7", "button8", "button9"
71 * Settings for a single axis.
74 axis () : value(0), offset(0.0), factor(1.0),
75 last_value(9999999), tolerance(0.002) {}
85 * Settings for a single button.
93 button () : value(0), step(0.0), action(ADJUST), isRepeatable(true),
104 * Settings for a single joystick.
107 virtual ~joystick () {
121 * Array of joystick settings.
123 static joystick joysticks[MAX_JOYSTICKS];
127 * Set up default values if properties are not specified.
132 SGPropertyList &props = current_properties;
134 // Default axis 0 to aileron
135 if (!props.getValue("/input/js0/axis0/control")) {
136 props.setStringValue("/input/js0/axis0/control", "/controls/aileron");
137 props.setDoubleValue("/input/js0/axis0/dead-band", 0.1);
140 // Default axis 1 to elevator
141 if (!props.getValue("/input/js0/axis1/control")) {
142 props.setStringValue("/input/js0/axis1/control", "/controls/elevator");
143 props.setDoubleValue("/input/js0/axis1/dead-band", 0.1);
144 props.setDoubleValue("/input/js0/axis1/factor", -1.0);
147 // Default axis 2 to rudder
148 if (!props.getValue("/input/js0/axis2/control")) {
149 props.setStringValue("/input/js0/axis2/control", "/controls/rudder");
150 props.setDoubleValue("/input/js0/axis2/dead-band", 0.1);
153 // Default axis 3 to throttle
154 // We need to fiddle with the offset
155 // and factor to make it work
156 if (!props.getValue("/input/js0/axis3/control")) {
157 props.setStringValue("/input/js0/axis3/control", "/controls/throttle");
158 props.setDoubleValue("/input/js0/axis3/dead-band", 0.0);
159 props.setDoubleValue("/input/js0/axis3/offset", -1.0);
160 props.setDoubleValue("/input/js0/axis3/factor", -0.5);
163 // Default button 0 to all brakes
164 if (!props.getValue("/input/js0/button0/control")) {
165 props.setStringValue("/input/js0/button0/action", "switch");
166 props.setStringValue("/input/js0/button0/control", "/controls/brakes/all");
167 props.setDoubleValue("/input/js0/button0/step", 1.0);
168 props.setDoubleValue("/input/js0/button0/repeatable", false);
171 // Default button 1 to left brake.
172 if (!props.getValue("/input/js0/button1/control")) {
173 props.setStringValue("/input/js0/button1/action", "switch");
174 props.setStringValue("/input/js0/button1/control",
175 "/controls/brakes/left");
176 props.setDoubleValue("/input/js0/button1/step", 1.0);
177 props.setDoubleValue("/input/js0/button1/repeatable", false);
180 // Default button 2 to right brake.
181 if (!props.getValue("/input/js0/button2/control")) {
182 props.setStringValue("/input/js0/button2/action", "switch");
183 props.setStringValue("/input/js0/button2/control",
184 "/controls/brakes/right");
185 props.setDoubleValue("/input/js0/button2/step", 1.0);
186 props.setDoubleValue("/input/js0/button2/repeatable", false);
189 // Default buttons 3 and 4 to elevator trim
190 if (!props.getValue("/input/js0/button3/control")) {
191 props.setStringValue("/input/js0/button3/action", "adjust");
192 props.setStringValue("/input/js0/button3/control",
193 "/controls/elevator-trim");
194 props.setDoubleValue("/input/js0/button3/step", 0.001);
195 props.setBoolValue("/input/js0/button3/repeatable", true);
197 if (!props.getValue("/input/js0/button4/control")) {
198 props.setStringValue("/input/js0/button4/action", "adjust");
199 props.setStringValue("/input/js0/button4/control",
200 "/controls/elevator-trim");
201 props.setDoubleValue("/input/js0/button4/step", -0.001);
202 props.setBoolValue("/input/js0/button4/repeatable", true);
205 // Default buttons 5 and 6 to flaps
206 if (!props.getValue("/input/js0/button5/control")) {
207 props.setStringValue("/input/js0/button5/action", "adjust");
208 props.setStringValue("/input/js0/button5/control", "/controls/flaps");
209 props.setDoubleValue("/input/js0/button5/step", -0.34);
210 props.setBoolValue("/input/js0/button5/repeatable", false);
212 if (!props.getValue("/input/js0/button6/control")) {
213 props.setStringValue("/input/js0/button6/action", "adjust");
214 props.setStringValue("/input/js0/button6/control", "/controls/flaps");
215 props.setDoubleValue("/input/js0/button6/step", 0.34);
216 props.setBoolValue("/input/js0/button6/repeatable", false);
222 * Initialize any joysticks found.
227 bool seen_joystick = false;
229 FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
233 for (int i = 0; i < MAX_JOYSTICKS; i++) {
234 jsJoystick * js = new jsJoystick(i);
235 joysticks[i].js = js;
236 if (js->notWorking()) {
237 FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found");
242 joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
243 int nbuttons = jsCaps.wNumButtons;
245 int nbuttons = MAX_BUTTONS;
248 int naxes = js->getNumAxes();
249 joysticks[i].naxes = naxes;
250 joysticks[i].nbuttons = nbuttons;
252 FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
253 seen_joystick = true;
255 // Set up range arrays
256 float *minRange = new float[naxes];
257 float *maxRange = new float[naxes];
258 float *center = new float[naxes];
260 // Initialize with default values
261 js->getMinRange(minRange);
262 js->getMaxRange(maxRange);
263 js->getCenter(center);
265 // Allocate axes and buttons
266 joysticks[i].axes = new axis[naxes];
267 joysticks[i].buttons = new button[nbuttons];
271 // Initialize the axes.
273 for (int j = 0; j < naxes; j++) {
274 axis &a = joysticks[i].axes[j];
276 string base = "/input/";
279 base += axisNames[j];
280 FG_LOG(FG_INPUT, FG_INFO, " Axis " << j << ':');
285 SGValue * value = current_properties.getValue(name);
287 FG_LOG(FG_INPUT, FG_INFO, " no control defined");
290 const string &control = value->getStringValue();
291 a.value = current_properties.getValue(control, true);
292 FG_LOG(FG_INPUT, FG_INFO, " using control " << control);
296 name += "/dead-band";
297 value = current_properties.getValue(name);
299 js->setDeadBand(j, value->getDoubleValue());
300 FG_LOG(FG_INPUT, FG_INFO, " dead-band is " << js->getDeadBand(j));
305 value = current_properties.getValue(name);
307 a.offset = value->getDoubleValue();
308 FG_LOG(FG_INPUT, FG_INFO, " offset is " << a.offset);
314 value = current_properties.getValue(name);
316 a.factor = value->getDoubleValue();
317 FG_LOG(FG_INPUT, FG_INFO, " factor is " << a.factor);
322 name += "/tolerance";
323 value = current_properties.getValue(name);
325 a.tolerance = value->getDoubleValue();
326 FG_LOG(FG_INPUT, FG_INFO, " tolerance is " << a.tolerance);
331 name += "/saturation";
332 value = current_properties.getValue(name);
334 js->setSaturation(j, value->getDoubleValue());
335 FG_LOG(FG_INPUT, FG_INFO, " saturation is " << js->getSaturation(j));
339 name += "/min-range";
340 value = current_properties.getValue(name);
342 minRange[j] = value->getDoubleValue();
343 FG_LOG(FG_INPUT, FG_INFO, " min-range is " << minRange[j]);
347 name += "/max-range";
348 value = current_properties.getValue(name);
350 maxRange[j] = value->getDoubleValue();
351 FG_LOG(FG_INPUT, FG_INFO, " max-range is " << maxRange[j]);
356 value = current_properties.getValue(name);
358 center[j] = value->getDoubleValue();
359 FG_LOG(FG_INPUT, FG_INFO, " center is " << center[j]);
364 // Initialize the buttons.
366 for (int j = 0; j < nbuttons; j++) {
367 button &b = joysticks[i].buttons[j];
369 string base = "/input/";
372 base += buttonNames[j];
373 FG_LOG(FG_INPUT, FG_INFO, " Button " << j << ':');
378 cout << "Trying name " << name << endl;
379 SGValue * value = current_properties.getValue(name);
381 FG_LOG(FG_INPUT, FG_INFO, " no control defined");
384 const string &control = value->getStringValue();
385 b.value = current_properties.getValue(control, true);
386 FG_LOG(FG_INPUT, FG_INFO, " using control " << control);
391 value = current_properties.getValue(name);
393 b.step = value->getDoubleValue();
394 FG_LOG(FG_INPUT, FG_INFO, " step is " << b.step);
399 value = current_properties.getValue(name);
400 string action = "adjust";
402 action = value->getStringValue();
403 if (action == "toggle") {
404 b.action = button::TOGGLE;
405 b.isRepeatable = false;
406 } else if (action == "switch") {
407 b.action = button::SWITCH;
408 b.isRepeatable = false;
409 } else if (action == "adjust") {
410 b.action = button::ADJUST;
411 b.isRepeatable = true;
413 FG_LOG(FG_INPUT, FG_ALERT, " unknown action " << action);
415 b.action = button::ADJUST;
416 b.isRepeatable = true;
418 FG_LOG(FG_INPUT, FG_INFO, " action is " << action);
422 name += "/repeatable";
423 value = current_properties.getValue(name);
425 b.isRepeatable = value->getBoolValue();
426 FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
427 " repeatable" : " not repeatable"));
430 js->setMinRange(minRange);
431 js->setMaxRange(maxRange);
432 js->setCenter(center);
441 FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
443 FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
445 return seen_joystick;
450 * Update property values based on the joystick state(s).
456 float *axis_values = new float[MAX_AXES];
458 for (int i = 0; i < MAX_JOYSTICKS; i++) {
459 jsJoystick * js = joysticks[i].js;
460 // float *axis_values = new float[joysticks[i].naxes];
461 if (js->notWorking()) {
465 js->read(&buttons, axis_values);
470 for (int j = 0; j < joysticks[i].naxes; j++) {
472 axis &a = joysticks[i].axes[j];
474 // If the axis hasn't changed, don't
476 if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
479 a.last_value = axis_values[j];
482 flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
485 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
486 << i << ", axis " << j);
492 for (int j = 0; j < joysticks[i].nbuttons; j++) {
494 button &b = joysticks[i].buttons[j];
499 if ((buttons & (1 << j)) > 0) {
501 if (b.lastState == 1 && !b.isRepeatable)
507 if (b.value->getDoubleValue() == 0.0)
508 flag = b.value->setDoubleValue(b.step);
510 flag = b.value->setDoubleValue(0.0);
512 if (b.value->getBoolValue())
513 flag = b.value->setBoolValue(false);
515 flag = b.value->setBoolValue(true);
519 flag = b.value->setDoubleValue(b.step);
522 if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
523 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
524 << i << ", axis " << j);
535 if (b.lastState == 0 && !b.isRepeatable)
543 flag = b.value->setDoubleValue(0.0);
556 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
557 << jsNames[i] << ' ' << buttonNames[j]);
567 // end of joystick.cxx