]> git.mxchange.org Git - flightgear.git/blob - src/Joystick/joystick.cxx
Fixes for MSVC++ issues.
[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 <math.h>
34
35 #include <string>
36
37 #include <simgear/misc/props.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <plib/js.h>
40 #include "joystick.hxx"
41
42 using std::string;
43
44 #ifdef WIN32
45 static const int MAX_JOYSTICKS = 2;
46 #else
47 static const int MAX_JOYSTICKS = 10;
48 #endif
49 static const int MAX_AXES = _JS_MAX_AXES;
50 static const int MAX_BUTTONS = 32;
51
52
53 /**
54  * Property names for joysticks and axes.
55  */
56 static const char * jsNames[] = {
57     "js0", "js1", "js2", "js3", "js4",
58     "js5", "js6", "js7", "js8", "js9"
59 };
60 static const char * axisNames[] = {
61     "axis0", "axis1", "axis2", "axis3", "axis4",
62     "axis5", "axis6", "axis7", "axis8", "axis9"
63 };
64 static const char * buttonNames[] = {
65     "button0", "button1", "button2", "button3", "button4",
66     "button5", "button6", "button7", "button8", "button9",
67     "button10", "button11", "button12", "button13", "button14",
68     "button15", "button16", "button17", "button18", "button19",
69     "button20", "button21", "button22", "button23", "button24",
70     "button25", "button26", "button27", "button28", "button29",
71     "button30", "button31"
72 };
73
74
75 /**
76  * Settings for a single axis.
77  */
78 struct axis {
79     axis () : value(0), offset(0.0), factor(1.0),
80         last_value(9999999), tolerance(0.002) {}
81     SGValue * value;
82     float offset;
83     float factor;
84     float last_value;
85     float tolerance;
86 };
87
88
89 /**
90  * Settings for a single button.
91  */
92 struct button {
93     enum Action {
94         TOGGLE,
95         SWITCH,
96         ADJUST
97     };
98     button () : value(0), step(0.0), action(ADJUST), isRepeatable(true),
99         lastState(-1) {}
100     SGValue * value;
101     float step;
102     Action action;
103     bool isRepeatable;
104     int lastState;
105 };
106
107
108 /**
109  * Settings for a single joystick.
110  */
111 struct joystick {
112     virtual ~joystick () {
113         delete js;
114         delete axes;
115         delete buttons;
116     }
117     int naxes;
118     int nbuttons;
119     jsJoystick * js;
120     axis * axes;
121     button * buttons;
122 };
123
124
125 /**
126  * Array of joystick settings.
127  */
128 static joystick joysticks[MAX_JOYSTICKS];
129
130
131 /**
132  * Set up default values if properties are not specified.
133  */
134 static void
135 setupDefaults ()
136 {
137     SGPropertyList &props = current_properties;
138
139     // Default axis 0 to aileron
140     if (!props.getValue("/input/js0/axis0/control")) {
141         props.setStringValue("/input/js0/axis0/control", "/controls/aileron");
142         props.setDoubleValue("/input/js0/axis0/dead-band", 0.1);
143     }
144
145     // Default axis 1 to elevator
146     if (!props.getValue("/input/js0/axis1/control")) {
147         props.setStringValue("/input/js0/axis1/control", "/controls/elevator");
148         props.setDoubleValue("/input/js0/axis1/dead-band", 0.1);
149         props.setDoubleValue("/input/js0/axis1/factor", -1.0);
150     }
151
152     // Default axis 2 to rudder
153     if (!props.getValue("/input/js0/axis2/control")) {
154         props.setStringValue("/input/js0/axis2/control", "/controls/rudder");
155         props.setDoubleValue("/input/js0/axis2/dead-band", 0.1);
156     }
157
158     // Default axis 3 to throttle
159     // We need to fiddle with the offset
160     // and factor to make it work
161     if (!props.getValue("/input/js0/axis3/control")) {
162         props.setStringValue("/input/js0/axis3/control", "/controls/throttle");
163         props.setDoubleValue("/input/js0/axis3/dead-band", 0.0);
164         props.setDoubleValue("/input/js0/axis3/offset", -1.0);
165         props.setDoubleValue("/input/js0/axis3/factor", -0.5);
166     }
167
168     // Default button 0 to all brakes
169     if (!props.getValue("/input/js0/button0/control")) {
170         props.setStringValue("/input/js0/button0/action", "switch");
171         props.setStringValue("/input/js0/button0/control", "/controls/brakes/all");
172         props.setDoubleValue("/input/js0/button0/step", 1.0);
173         props.setDoubleValue("/input/js0/button0/repeatable", false);
174     }
175
176     // Default button 1 to left brake.
177     if (!props.getValue("/input/js0/button1/control")) {
178         props.setStringValue("/input/js0/button1/action", "switch");
179         props.setStringValue("/input/js0/button1/control",
180                              "/controls/brakes/left");
181         props.setDoubleValue("/input/js0/button1/step", 1.0);
182         props.setDoubleValue("/input/js0/button1/repeatable", false);
183     }
184
185     // Default button 2 to right brake.
186     if (!props.getValue("/input/js0/button2/control")) {
187         props.setStringValue("/input/js0/button2/action", "switch");
188         props.setStringValue("/input/js0/button2/control",
189                              "/controls/brakes/right");
190         props.setDoubleValue("/input/js0/button2/step", 1.0);
191         props.setDoubleValue("/input/js0/button2/repeatable", false);
192     }
193
194     // Default buttons 3 and 4 to elevator trim
195     if (!props.getValue("/input/js0/button3/control")) {
196         props.setStringValue("/input/js0/button3/action", "adjust");
197         props.setStringValue("/input/js0/button3/control",
198                              "/controls/elevator-trim");
199         props.setDoubleValue("/input/js0/button3/step", 0.001);
200         props.setBoolValue("/input/js0/button3/repeatable", true);
201     }
202     if (!props.getValue("/input/js0/button4/control")) {
203         props.setStringValue("/input/js0/button4/action", "adjust");
204         props.setStringValue("/input/js0/button4/control",
205                              "/controls/elevator-trim");
206         props.setDoubleValue("/input/js0/button4/step", -0.001);
207         props.setBoolValue("/input/js0/button4/repeatable", true);
208     }
209
210     // Default buttons 5 and 6 to flaps
211     if (!props.getValue("/input/js0/button5/control")) {
212         props.setStringValue("/input/js0/button5/action", "adjust");
213         props.setStringValue("/input/js0/button5/control", "/controls/flaps");
214         props.setDoubleValue("/input/js0/button5/step", -0.34);
215         props.setBoolValue("/input/js0/button5/repeatable", false);
216     }
217     if (!props.getValue("/input/js0/button6/control")) {
218         props.setStringValue("/input/js0/button6/action", "adjust");
219         props.setStringValue("/input/js0/button6/control", "/controls/flaps");
220         props.setDoubleValue("/input/js0/button6/step", 0.34);
221         props.setBoolValue("/input/js0/button6/repeatable", false);
222     }
223 }
224
225
226 /**
227  * Initialize any joysticks found.
228  */
229 int
230 fgJoystickInit() 
231 {
232     bool seen_joystick = false;
233
234     FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
235
236     setupDefaults();
237
238     for (int i = 0; i < MAX_JOYSTICKS; i++) {
239         jsJoystick * js = new jsJoystick(i);
240         joysticks[i].js = js;
241         if (js->notWorking()) {
242             FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found");
243             continue;
244         }
245 #ifdef WIN32
246         JOYCAPS jsCaps ;
247         joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
248         int nbuttons = jsCaps.wNumButtons;
249 #else
250         int nbuttons = MAX_BUTTONS;
251 #endif
252         
253         int naxes = js->getNumAxes();
254         joysticks[i].naxes = naxes;
255         joysticks[i].nbuttons = nbuttons;
256
257         FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
258         seen_joystick = true;
259
260         // Set up range arrays
261         float *minRange = new float[naxes];
262         float *maxRange = new float[naxes];
263         float *center = new float[naxes];
264
265         // Initialize with default values
266         js->getMinRange(minRange);
267         js->getMaxRange(maxRange);
268         js->getCenter(center);
269
270         // Allocate axes and buttons
271         joysticks[i].axes = new axis[naxes];
272         joysticks[i].buttons = new button[nbuttons];
273
274
275         //
276         // Initialize the axes.
277         //
278         int j;
279         for (j = 0; j < naxes; j++) {
280             axis &a = joysticks[i].axes[j];
281
282             string base = "/input/";
283             base += jsNames[i];
284             base += '/';
285             base += axisNames[j];
286             FG_LOG(FG_INPUT, FG_INFO, "  Axis " << j << ':');
287
288             // Control property
289             string name = base;
290             name += "/control";
291             SGValue * value = current_properties.getValue(name);
292             if (value == 0) {
293                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
294                 continue;
295             }
296             const string &control = value->getStringValue();
297             a.value = current_properties.getValue(control, true);
298             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
299
300             // Dead band
301             name = base;
302             name += "/dead-band";
303             value = current_properties.getValue(name);
304             if (value != 0)
305                 js->setDeadBand(j, value->getDoubleValue());
306             FG_LOG(FG_INPUT, FG_INFO, "    dead-band is " << js->getDeadBand(j));
307
308             // Offset
309             name = base;
310             name += "/offset";
311             value = current_properties.getValue(name);
312             if (value != 0)
313                 a.offset = value->getDoubleValue();
314             FG_LOG(FG_INPUT, FG_INFO, "    offset is " << a.offset);
315
316
317             // Factor
318             name = base;
319             name += "/factor";
320             value = current_properties.getValue(name);
321             if (value != 0)
322                 a.factor = value->getDoubleValue();
323             FG_LOG(FG_INPUT, FG_INFO, "    factor is " << a.factor);
324
325
326             // Tolerance
327             name = base;
328             name += "/tolerance";
329             value = current_properties.getValue(name);
330             if (value != 0)
331                 a.tolerance = value->getDoubleValue();
332             FG_LOG(FG_INPUT, FG_INFO, "    tolerance is " << a.tolerance);
333
334
335             // Saturation
336             name = base;
337             name += "/saturation";
338             value = current_properties.getValue(name);
339             if (value != 0)
340                 js->setSaturation(j, value->getDoubleValue());
341             FG_LOG(FG_INPUT, FG_INFO, "    saturation is " << js->getSaturation(j));
342
343             // Minimum range
344             name = base;
345             name += "/min-range";
346             value = current_properties.getValue(name);
347             if (value != 0)
348                 minRange[j] = value->getDoubleValue();
349             FG_LOG(FG_INPUT, FG_INFO, "    min-range is " << minRange[j]);
350
351             // Maximum range
352             name = base;
353             name += "/max-range";
354             value = current_properties.getValue(name);
355             if (value != 0)
356                 maxRange[j] = value->getDoubleValue();
357             FG_LOG(FG_INPUT, FG_INFO, "    max-range is " << maxRange[j]);
358
359             // Center
360             name = base;
361             name += "/center";
362             value = current_properties.getValue(name);
363             if (value != 0)
364                 center[j] = value->getDoubleValue();
365             FG_LOG(FG_INPUT, FG_INFO, "    center is " << center[j]);
366         }
367
368
369         //
370         // Initialize the buttons.
371         //
372         for (j = 0; j < nbuttons; j++) {
373             button &b = joysticks[i].buttons[j];
374       
375             string base = "/input/";
376             base += jsNames[i];
377             base += '/';
378             base += buttonNames[j];
379             FG_LOG(FG_INPUT, FG_INFO, "  Button " << j << ':');
380
381             // Control property
382             string name = base;
383             name += "/control";
384             cout << "Trying name " << name << endl;
385             SGValue * value = current_properties.getValue(name);
386             if (value == 0) {
387                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
388                 continue;
389             }
390             const string &control = value->getStringValue();
391             b.value = current_properties.getValue(control, true);
392             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
393
394             // Step
395             name = base;
396             name += "/step";
397             value = current_properties.getValue(name);
398             if (value != 0)
399                 b.step = value->getDoubleValue();
400             FG_LOG(FG_INPUT, FG_INFO, "    step is " << b.step);
401
402             // Type
403             name = base;
404             name += "/action";
405             value = current_properties.getValue(name);
406             string action = "adjust";
407             if (value != 0)
408                 action = value->getStringValue();
409             if (action == "toggle") {
410                 b.action = button::TOGGLE;
411                 b.isRepeatable = false;
412             } else if (action == "switch") {
413                 b.action = button::SWITCH;
414                 b.isRepeatable = false;
415             } else if (action == "adjust") {
416                 b.action = button::ADJUST;
417                 b.isRepeatable = true;
418             } else {
419                 FG_LOG(FG_INPUT, FG_ALERT, "    unknown action " << action);
420                 action = "adjust";
421                 b.action = button::ADJUST;
422                 b.isRepeatable = true;
423             }
424             FG_LOG(FG_INPUT, FG_INFO, "    action is " << action);
425
426             // Repeatability.
427             name = base;
428             name += "/repeatable";
429             value = current_properties.getValue(name);
430             if (value != 0)
431                 b.isRepeatable = value->getBoolValue();
432             FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
433                                        "    repeatable" : "    not repeatable"));
434         }
435
436         js->setMinRange(minRange);
437         js->setMaxRange(maxRange);
438         js->setCenter(center);
439
440         //-dw- clean up
441         delete minRange;
442         delete maxRange;
443         delete center;
444     }
445
446     if (seen_joystick)
447         FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
448     else
449         FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
450
451     return seen_joystick;
452 }
453
454
455 /**
456  * Update property values based on the joystick state(s).
457  */
458 int
459 fgJoystickRead()
460 {
461     int buttons;
462     float *axis_values = new float[MAX_AXES];
463
464     for (int i = 0; i < MAX_JOYSTICKS; i++) {
465         jsJoystick * js = joysticks[i].js;
466         // float *axis_values = new float[joysticks[i].naxes];
467         if (js->notWorking()) {
468             continue;
469         }
470
471         js->read(&buttons, axis_values);
472
473         //
474         // Axes
475         //
476         int j;
477         for (j = 0; j < joysticks[i].naxes; j++) {
478             bool flag = true;
479             axis &a = joysticks[i].axes[j];
480
481             // If the axis hasn't changed, don't
482             // force the value.
483             if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
484                 continue;
485             else
486                 a.last_value = axis_values[j];
487
488             if (a.value)
489                 flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
490                                                a.factor);
491             if (!flag)
492                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
493                        << i << ", axis " << j);
494         }
495
496         //
497         // Buttons
498         //
499         for (j = 0; j < joysticks[i].nbuttons; j++) {
500             bool flag;
501             button &b = joysticks[i].buttons[j];
502             if (b.value == 0)
503                 continue;
504
505             // Button is on.
506             if ((buttons & (1 << j)) > 0) {
507                 // Repeating?
508                 if (b.lastState == 1 && !b.isRepeatable)
509                     continue;
510
511                 switch (b.action) {
512                 case button::TOGGLE:
513                     if (b.step != 0.0) {
514                         if (b.value->getDoubleValue() == 0.0)
515                             flag = b.value->setDoubleValue(b.step);
516                         else
517                             flag = b.value->setDoubleValue(0.0);
518                     } else {
519                         if (b.value->getBoolValue())
520                             flag = b.value->setBoolValue(false);
521                         else
522                             flag = b.value->setBoolValue(true);
523                     }
524                     break;
525                 case button::SWITCH:
526                     flag = b.value->setDoubleValue(b.step);
527                     break;
528                 case button::ADJUST:
529                     if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
530                         FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
531                                << i << ", axis " << j);
532                     break;
533                 default:
534                     flag = false;
535                     break;
536                 }
537                 b.lastState = 1;
538
539                 // Button is off
540             } else {
541                 // Repeating?
542                 if (b.lastState == 0 && !b.isRepeatable)
543                     continue;
544
545                 switch (b.action) {
546                 case button::TOGGLE:
547                     // no op
548                     break;
549                 case button::SWITCH:
550                     flag = b.value->setDoubleValue(0.0);
551                     break;
552                 case button::ADJUST:
553                     // no op
554                     break;
555                 default:
556                     flag = false;
557                     break;
558                 }
559
560                 b.lastState = 0;
561             }
562             if (!flag)
563                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
564                        << jsNames[i] << ' ' << buttonNames[j]);
565         }
566     }
567
568     // -dw- cleanup 
569     delete axis_values;
570
571     return true;
572 }
573
574 // end of joystick.cxx