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.
35 #include <simgear/misc/props.hxx>
36 #include <simgear/debug/logstream.hxx>
38 #include "joystick.hxx"
42 static const int MAX_JOYSTICKS = 10;
43 static const int MAX_AXES = 10;
44 static const int MAX_BUTTONS = 10;
48 * Property names for joysticks and axes.
50 static const char * jsNames[] = {
51 "js0", "js1", "js2", "js3", "js4",
52 "js5", "js6", "js7", "js8", "js9"
54 static const char * axisNames[] = {
55 "axis0", "axis1", "axis2", "axis3", "axis4",
56 "axis5", "axis6", "axis7", "axis8", "axis9"
58 static const char * buttonNames[] = {
59 "button0", "button1", "button2", "button3", "button4",
60 "button5", "button6", "button7", "button8", "button9"
65 * Settings for a single axis.
68 axis () : value(0), offset(0.0), factor(1.0),
69 last_value(9999999), tolerance(0.002) {}
79 * Settings for a single button.
87 button () : value(0), step(0.0), action(ADJUST), isRepeatable(true),
98 * Settings for a single joystick.
101 virtual ~joystick () { delete js; delete axes; }
109 * Array of joystick settings.
111 static joystick joysticks[MAX_JOYSTICKS];
115 * Set up default values if properties are not specified.
120 SGPropertyList &props = current_properties;
122 // Default axis 0 to aileron
123 if (!props.getValue("/input/js0/axis0/control")) {
124 props.setStringValue("/input/js0/axis0/control", "/controls/aileron");
125 props.setFloatValue("/input/js0/axis0/dead-band", 0.1);
128 // Default axis 1 to elevator
129 if (!props.getValue("/input/js0/axis1/control")) {
130 props.setStringValue("/input/js0/axis1/control", "/controls/elevator");
131 props.setFloatValue("/input/js0/axis1/dead-band", 0.1);
132 props.setFloatValue("/input/js0/axis1/factor", -1.0);
135 // Default axis 2 to throttle
136 // We need to fiddle with the offset
137 // and factor to make it work
138 if (!props.getValue("/input/js0/axis2/control")) {
139 props.setStringValue("/input/js0/axis2/control", "/controls/throttle");
140 props.setFloatValue("/input/js0/axis2/dead-band", 0.0);
141 props.setFloatValue("/input/js0/axis2/offset", -1.0);
142 props.setFloatValue("/input/js0/axis2/factor", -0.5);
145 // Default axis 3 to rudder
146 if (!props.getValue("/input/js0/axis3/control")) {
147 props.setStringValue("/input/js0/axis3/control", "/controls/rudder");
148 props.setFloatValue("/input/js0/axis3/dead-band", 0.3);
151 // Default button 0 to all brakes
152 if (!props.getValue("/input/js0/button0/control")) {
153 props.setStringValue("/input/js0/button0/action", "switch");
154 props.setStringValue("/input/js0/button0/control", "/controls/brake");
155 props.setFloatValue("/input/js0/button0/step", 1.0);
156 props.setFloatValue("/input/js0/button0/repeatable", false);
159 // Default button 1 to left brake.
160 if (!props.getValue("/input/js0/button1/control")) {
161 props.setStringValue("/input/js0/button1/action", "switch");
162 props.setStringValue("/input/js0/button1/control", "/controls/left-brake");
163 props.setFloatValue("/input/js0/button1/step", 1.0);
164 props.setFloatValue("/input/js0/button1/repeatable", false);
167 // Default button 2 to right brake.
168 if (!props.getValue("/input/js0/button2/control")) {
169 props.setStringValue("/input/js0/button2/action", "switch");
170 props.setStringValue("/input/js0/button2/control",
171 "/controls/right-brake");
172 props.setFloatValue("/input/js0/button2/step", 1.0);
173 props.setFloatValue("/input/js0/button2/repeatable", false);
176 // Default buttons 3 and 4 to elevator trim
177 if (!props.getValue("/input/js0/button3/control")) {
178 props.setStringValue("/input/js0/button3/action", "adjust");
179 props.setStringValue("/input/js0/button3/control",
180 "/controls/elevator-trim");
181 props.setFloatValue("/input/js0/button3/step", 0.001);
182 props.setBoolValue("/input/js0/button3/repeatable", true);
184 if (!props.getValue("/input/js0/button4/control")) {
185 props.setStringValue("/input/js0/button4/action", "adjust");
186 props.setStringValue("/input/js0/button4/control",
187 "/controls/elevator-trim");
188 props.setFloatValue("/input/js0/button4/step", -0.001);
189 props.setBoolValue("/input/js0/button4/repeatable", true);
192 // Default buttons 5 and 6 to flaps
193 if (!props.getValue("/input/js0/button5/control")) {
194 props.setStringValue("/input/js0/button5/action", "adjust");
195 props.setStringValue("/input/js0/button5/control", "/controls/flaps");
196 props.setFloatValue("/input/js0/button5/step", -0.34);
197 props.setBoolValue("/input/js0/button5/repeatable", false);
199 if (!props.getValue("/input/js0/button6/control")) {
200 props.setStringValue("/input/js0/button6/action", "adjust");
201 props.setStringValue("/input/js0/button6/control", "/controls/flaps");
202 props.setFloatValue("/input/js0/button6/step", 0.34);
203 props.setBoolValue("/input/js0/button6/repeatable", false);
209 * Initialize any joysticks found.
214 bool seen_joystick = false;
216 FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
220 for (int i = 0; i < MAX_JOYSTICKS; i++) {
221 jsJoystick * js = new jsJoystick(i);
222 joysticks[i].js = js;
223 if (js->notWorking()) {
224 FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found");
228 FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
229 seen_joystick = true;
231 // Set up range arrays
232 float minRange[js->getNumAxes()];
233 float maxRange[js->getNumAxes()];
234 float center[js->getNumAxes()];
236 // Initialize with default values
237 js->getMinRange(minRange);
238 js->getMaxRange(maxRange);
239 js->getCenter(center);
241 // Allocate axes and buttons
242 joysticks[i].axes = new axis[js->getNumAxes()];
243 joysticks[i].buttons = new button[MAX_BUTTONS];
247 // Initialize the axes.
249 for (int j = 0; j < min(js->getNumAxes(), MAX_AXES); j++) {
250 axis &a = joysticks[i].axes[j];
252 string base = "/input/";
255 base += axisNames[j];
256 FG_LOG(FG_INPUT, FG_INFO, " Axis " << j << ':');
261 SGValue * value = current_properties.getValue(name);
263 FG_LOG(FG_INPUT, FG_INFO, " no control defined");
266 const string &control = value->getStringValue();
267 a.value = current_properties.getValue(control, true);
268 FG_LOG(FG_INPUT, FG_INFO, " using control " << control);
272 name += "/dead-band";
273 value = current_properties.getValue(name);
275 js->setDeadBand(j, value->getFloatValue());
276 FG_LOG(FG_INPUT, FG_INFO, " dead-band is " << js->getDeadBand(j));
281 value = current_properties.getValue(name);
283 a.offset = value->getFloatValue();
284 FG_LOG(FG_INPUT, FG_INFO, " offset is " << a.offset);
290 value = current_properties.getValue(name);
292 a.factor = value->getFloatValue();
293 FG_LOG(FG_INPUT, FG_INFO, " factor is " << a.factor);
298 name += "/tolerance";
299 value = current_properties.getValue(name);
301 a.tolerance = value->getFloatValue();
302 FG_LOG(FG_INPUT, FG_INFO, " tolerance is " << a.tolerance);
307 name += "/saturation";
308 value = current_properties.getValue(name);
310 js->setSaturation(j, value->getFloatValue());
311 FG_LOG(FG_INPUT, FG_INFO, " saturation is " << js->getSaturation(j));
315 name += "/min-range";
316 value = current_properties.getValue(name);
318 minRange[j] = value->getFloatValue();
319 FG_LOG(FG_INPUT, FG_INFO, " min-range is " << minRange[j]);
323 name += "/max-range";
324 value = current_properties.getValue(name);
326 maxRange[j] = value->getFloatValue();
327 FG_LOG(FG_INPUT, FG_INFO, " max-range is " << maxRange[j]);
332 value = current_properties.getValue(name);
334 center[j] = value->getFloatValue();
335 FG_LOG(FG_INPUT, FG_INFO, " center is " << center[j]);
340 // Initialize the buttons.
342 for (int j = 0; j < MAX_BUTTONS; j++) {
343 button &b = joysticks[i].buttons[j];
345 string base = "/input/";
348 base += buttonNames[j];
349 FG_LOG(FG_INPUT, FG_INFO, " Button " << j << ':');
354 cout << "Trying name " << name << endl;
355 SGValue * value = current_properties.getValue(name);
357 FG_LOG(FG_INPUT, FG_INFO, " no control defined");
360 const string &control = value->getStringValue();
361 b.value = current_properties.getValue(control, true);
362 FG_LOG(FG_INPUT, FG_INFO, " using control " << control);
367 value = current_properties.getValue(name);
369 b.step = value->getFloatValue();
370 FG_LOG(FG_INPUT, FG_INFO, " step is " << b.step);
375 value = current_properties.getValue(name);
376 string action = "adjust";
378 action = value->getStringValue();
379 if (action == "toggle") {
380 b.action = button::TOGGLE;
381 b.isRepeatable = false;
382 } else if (action == "switch") {
383 b.action = button::SWITCH;
384 b.isRepeatable = false;
385 } else if (action == "adjust") {
386 b.action = button::ADJUST;
387 b.isRepeatable = true;
389 FG_LOG(FG_INPUT, FG_ALERT, " unknown action " << action);
391 b.action = button::ADJUST;
392 b.isRepeatable = true;
394 FG_LOG(FG_INPUT, FG_INFO, " action is " << action);
398 name += "/repeatable";
399 value = current_properties.getValue(name);
401 b.isRepeatable = value->getBoolValue();
402 FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
403 " repeatable" : " not repeatable"));
406 js->setMinRange(minRange);
407 js->setMaxRange(maxRange);
408 js->setCenter(center);
412 FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
414 FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
416 return seen_joystick;
421 * Update property values based on the joystick state(s).
428 for (int i = 0; i < MAX_JOYSTICKS; i++) {
429 jsJoystick * js = joysticks[i].js;
430 float axis_values[js->getNumAxes()];
431 if (js->notWorking()) {
435 js->read(&buttons, axis_values);
440 for (int j = 0; j < min(MAX_AXES, js->getNumAxes()); j++) {
442 axis &a = joysticks[i].axes[j];
444 // If the axis hasn't changed, don't
446 if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
449 a.last_value = axis_values[j];
452 flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
455 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
456 << i << ", axis " << j);
462 for (int j = 0; j < MAX_BUTTONS; j++) {
464 button &b = joysticks[i].buttons[j];
469 if ((buttons & (1 << j)) > 0) {
471 if (b.lastState == 1 && !b.isRepeatable)
477 if (b.value->getDoubleValue() == 0.0)
478 flag = b.value->setDoubleValue(b.step);
480 flag = b.value->setDoubleValue(0.0);
482 if (b.value->getBoolValue())
483 flag = b.value->setBoolValue(false);
485 flag = b.value->setBoolValue(true);
489 flag = b.value->setDoubleValue(b.step);
492 if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
493 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
494 << i << ", axis " << j);
505 if (b.lastState == 0 && !b.isRepeatable)
513 flag = b.value->setDoubleValue(0.0);
526 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
527 << jsNames[i] << ' ' << buttonNames[j]);
534 // end of joystick.cxx