]> git.mxchange.org Git - flightgear.git/blob - src/Joystick/joystick.cxx
Renamed the property manager stuff from fg* to sg*
[flightgear.git] / src / Joystick / joystick.cxx
1 // joystick.cxx -- joystick support
2 //
3 // Original module written by Curtis Olson, started October 1998.
4 // Completely rewritten by David Megginson, July 2000.
5 //
6 // Copyright (C) 1998 - 2000  Curtis L. Olson - curt@flightgear.org
7 //
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.
12 //
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.
17 //
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.
21 //
22 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #ifdef HAVE_WINDOWS_H
30 #  include <windows.h>                     
31 #endif
32
33 #include <string>
34
35 #include <simgear/misc/props.hxx>
36 #include <simgear/debug/logstream.hxx>
37 #include <plib/js.h>
38 #include "joystick.hxx"
39
40 using std::string;
41
42 static const int MAX_JOYSTICKS = 10;
43 static const int MAX_AXES = 10;
44 static const int MAX_BUTTONS = 10;
45
46
47 /**
48  * Property names for joysticks and axes.
49  */
50 static const char * jsNames[] = {
51   "js0", "js1", "js2", "js3", "js4",
52   "js5", "js6", "js7", "js8", "js9"
53 };
54 static const char * axisNames[] = {
55   "axis0", "axis1", "axis2", "axis3", "axis4",
56   "axis5", "axis6", "axis7", "axis8", "axis9"
57 };
58 static const char * buttonNames[] = {
59   "button0", "button1", "button2", "button3", "button4",
60   "button5", "button6", "button7", "button8", "button9"
61 };
62
63
64 /**
65  * Settings for a single axis.
66  */
67 struct axis {
68   axis () : value(0), offset(0.0), factor(1.0),
69             last_value(9999999), tolerance(0.002) {}
70   SGValue * value;
71   float offset;
72   float factor;
73   float last_value;
74   float tolerance;
75 };
76
77
78 /**
79  * Settings for a single button.
80  */
81 struct button {
82   enum Action {
83     TOGGLE,
84     SWITCH,
85     ADJUST
86   };
87   button () : value(0), step(0.0), action(ADJUST), isRepeatable(true),
88               lastState(-1) {}
89   SGValue * value;
90   float step;
91   Action action;
92   bool isRepeatable;
93   int lastState;
94 };
95
96
97 /**
98  * Settings for a single joystick.
99  */
100 struct joystick {
101   virtual ~joystick () { delete js; delete axes; }
102   jsJoystick * js;
103   axis * axes;
104   button * buttons;
105 };
106
107
108 /**
109  * Array of joystick settings.
110  */
111 static joystick joysticks[MAX_JOYSTICKS];
112
113
114 /**
115  * Set up default values if properties are not specified.
116  */
117 static void
118 setupDefaults ()
119 {
120   SGPropertyList &props = current_properties;
121
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);
126   }
127
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);
133   }
134
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);
143   }
144
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);
149   }
150
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);
157   }
158
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);
165   }
166
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);
174   }
175
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);
183   }
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);
190   }
191
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);
198   }
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);
204   }
205 }
206
207
208 /**
209  * Initialize any joysticks found.
210  */
211 int
212 fgJoystickInit() 
213 {
214   bool seen_joystick = false;
215
216   FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
217
218   setupDefaults();
219
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");
225       continue;
226     }
227
228     FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
229     seen_joystick = true;
230
231                                 // Set up range arrays
232     float minRange[js->getNumAxes()];
233     float maxRange[js->getNumAxes()];
234     float center[js->getNumAxes()];
235
236                                 // Initialize with default values
237     js->getMinRange(minRange);
238     js->getMaxRange(maxRange);
239     js->getCenter(center);
240
241                                 // Allocate axes and buttons
242     joysticks[i].axes = new axis[js->getNumAxes()];
243     joysticks[i].buttons = new button[MAX_BUTTONS];
244
245
246     //
247     // Initialize the axes.
248     //
249     for (int j = 0; j < min(js->getNumAxes(), MAX_AXES); j++) {
250       axis &a = joysticks[i].axes[j];
251
252       string base = "/input/";
253       base += jsNames[i];
254       base += '/';
255       base += axisNames[j];
256       FG_LOG(FG_INPUT, FG_INFO, "  Axis " << j << ':');
257
258                                 // Control property
259       string name = base;
260       name += "/control";
261       SGValue * value = current_properties.getValue(name);
262       if (value == 0) {
263         FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
264         continue;
265       }
266       const string &control = value->getStringValue();
267       a.value = current_properties.getValue(control, true);
268       FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
269
270                                 // Dead band
271       name = base;
272       name += "/dead-band";
273       value = current_properties.getValue(name);
274       if (value != 0)
275         js->setDeadBand(j, value->getFloatValue());
276       FG_LOG(FG_INPUT, FG_INFO, "    dead-band is " << js->getDeadBand(j));
277
278                                 // Offset
279       name = base;
280       name += "/offset";
281       value = current_properties.getValue(name);
282       if (value != 0)
283         a.offset = value->getFloatValue();
284       FG_LOG(FG_INPUT, FG_INFO, "    offset is " << a.offset);
285
286
287                                 // Factor
288       name = base;
289       name += "/factor";
290       value = current_properties.getValue(name);
291       if (value != 0)
292         a.factor = value->getFloatValue();
293       FG_LOG(FG_INPUT, FG_INFO, "    factor is " << a.factor);
294
295
296                                 // Tolerance
297       name = base;
298       name += "/tolerance";
299       value = current_properties.getValue(name);
300       if (value != 0)
301         a.tolerance = value->getFloatValue();
302       FG_LOG(FG_INPUT, FG_INFO, "    tolerance is " << a.tolerance);
303
304
305                                 // Saturation
306       name = base;
307       name += "/saturation";
308       value = current_properties.getValue(name);
309       if (value != 0)
310         js->setSaturation(j, value->getFloatValue());
311       FG_LOG(FG_INPUT, FG_INFO, "    saturation is " << js->getSaturation(j));
312
313                                 // Minimum range
314       name = base;
315       name += "/min-range";
316       value = current_properties.getValue(name);
317       if (value != 0)
318         minRange[j] = value->getFloatValue();
319       FG_LOG(FG_INPUT, FG_INFO, "    min-range is " << minRange[j]);
320
321                                 // Maximum range
322       name = base;
323       name += "/max-range";
324       value = current_properties.getValue(name);
325       if (value != 0)
326         maxRange[j] = value->getFloatValue();
327       FG_LOG(FG_INPUT, FG_INFO, "    max-range is " << maxRange[j]);
328
329                                 // Center
330       name = base;
331       name += "/center";
332       value = current_properties.getValue(name);
333       if (value != 0)
334         center[j] = value->getFloatValue();
335       FG_LOG(FG_INPUT, FG_INFO, "    center is " << center[j]);
336     }
337
338
339     //
340     // Initialize the buttons.
341     //
342     for (int j = 0; j < MAX_BUTTONS; j++) {
343       button &b = joysticks[i].buttons[j];
344       
345       string base = "/input/";
346       base += jsNames[i];
347       base += '/';
348       base += buttonNames[j];
349       FG_LOG(FG_INPUT, FG_INFO, "  Button " << j << ':');
350
351                                 // Control property
352       string name = base;
353       name += "/control";
354       cout << "Trying name " << name << endl;
355       SGValue * value = current_properties.getValue(name);
356       if (value == 0) {
357         FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
358         continue;
359       }
360       const string &control = value->getStringValue();
361       b.value = current_properties.getValue(control, true);
362       FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
363
364                                 // Step
365       name = base;
366       name += "/step";
367       value = current_properties.getValue(name);
368       if (value != 0)
369         b.step = value->getFloatValue();
370       FG_LOG(FG_INPUT, FG_INFO, "    step is " << b.step);
371
372                                 // Type
373       name = base;
374       name += "/action";
375       value = current_properties.getValue(name);
376       string action = "adjust";
377       if (value != 0)
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;
388       } else {
389         FG_LOG(FG_INPUT, FG_ALERT, "    unknown action " << action);
390         action = "adjust";
391         b.action = button::ADJUST;
392         b.isRepeatable = true;
393       }
394       FG_LOG(FG_INPUT, FG_INFO, "    action is " << action);
395
396                                 // Repeatability.
397       name = base;
398       name += "/repeatable";
399       value = current_properties.getValue(name);
400       if (value != 0)
401         b.isRepeatable = value->getBoolValue();
402       FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
403              "    repeatable" : "    not repeatable"));
404     }
405
406     js->setMinRange(minRange);
407     js->setMaxRange(maxRange);
408     js->setCenter(center);
409   }
410
411   if (seen_joystick)
412     FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
413   else
414     FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
415
416   return seen_joystick;
417 }
418
419
420 /**
421  * Update property values based on the joystick state(s).
422  */
423 int
424 fgJoystickRead()
425 {
426   int buttons;
427
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()) {
432       continue;
433     }
434
435     js->read(&buttons, axis_values);
436
437     //
438     // Axes
439     //
440     for (int j = 0; j < min(MAX_AXES, js->getNumAxes()); j++) {
441       bool flag = true;
442       axis &a = joysticks[i].axes[j];
443
444                                 // If the axis hasn't changed, don't
445                                 // force the value.
446       if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
447         continue;
448       else
449         a.last_value = axis_values[j];
450
451       if (a.value)
452         flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
453                                        a.factor);
454       if (!flag)
455         FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
456                << i << ", axis " << j);
457     }
458
459     //
460     // Buttons
461     //
462     for (int j = 0; j < MAX_BUTTONS; j++) {
463       bool flag;
464       button &b = joysticks[i].buttons[j];
465       if (b.value == 0)
466         continue;
467
468                                 // Button is on.
469       if ((buttons & (1 << j)) > 0) {
470                                 // Repeating?
471         if (b.lastState == 1 && !b.isRepeatable)
472           continue;
473
474         switch (b.action) {
475         case button::TOGGLE:
476           if (b.step != 0.0) {
477             if (b.value->getDoubleValue() == 0.0)
478               flag = b.value->setDoubleValue(b.step);
479             else
480               flag = b.value->setDoubleValue(0.0);
481           } else {
482             if (b.value->getBoolValue())
483               flag = b.value->setBoolValue(false);
484             else
485               flag = b.value->setBoolValue(true);
486           }
487           break;
488         case button::SWITCH:
489           flag = b.value->setDoubleValue(b.step);
490           break;
491         case button::ADJUST:
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);
495           break;
496         default:
497           flag = false;
498           break;
499         }
500         b.lastState = 1;
501
502                                 // Button is off
503       } else {
504                                 // Repeating?
505         if (b.lastState == 0 && !b.isRepeatable)
506           continue;
507
508         switch (b.action) {
509         case button::TOGGLE:
510           // no op
511           break;
512         case button::SWITCH:
513           flag = b.value->setDoubleValue(0.0);
514           break;
515         case button::ADJUST:
516           // no op
517           break;
518         default:
519           flag = false;
520           break;
521         }
522
523         b.lastState = 0;
524       }
525       if (!flag)
526         FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
527                << jsNames[i] << ' ' << buttonNames[j]);
528     }
529   }
530
531   return true;
532 }
533
534 // end of joystick.cxx
535