]> git.mxchange.org Git - flightgear.git/blob - src/Joystick/joystick.cxx
Fixes to jsbsim.
[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 = 10;
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 };
68
69
70 /**
71  * Settings for a single axis.
72  */
73 struct axis {
74     axis () : value(0), offset(0.0), factor(1.0),
75         last_value(9999999), tolerance(0.002) {}
76     SGValue * value;
77     float offset;
78     float factor;
79     float last_value;
80     float tolerance;
81 };
82
83
84 /**
85  * Settings for a single button.
86  */
87 struct button {
88     enum Action {
89         TOGGLE,
90         SWITCH,
91         ADJUST
92     };
93     button () : value(0), step(0.0), action(ADJUST), isRepeatable(true),
94         lastState(-1) {}
95     SGValue * value;
96     float step;
97     Action action;
98     bool isRepeatable;
99     int lastState;
100 };
101
102
103 /**
104  * Settings for a single joystick.
105  */
106 struct joystick {
107     virtual ~joystick () {
108         delete js;
109         delete axes;
110         delete buttons;
111     }
112     int naxes;
113     int nbuttons;
114     jsJoystick * js;
115     axis * axes;
116     button * buttons;
117 };
118
119
120 /**
121  * Array of joystick settings.
122  */
123 static joystick joysticks[MAX_JOYSTICKS];
124
125
126 /**
127  * Set up default values if properties are not specified.
128  */
129 static void
130 setupDefaults ()
131 {
132     SGPropertyList &props = current_properties;
133
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);
138     }
139
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);
145     }
146
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);
151     }
152
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);
161     }
162
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);
169     }
170
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);
178     }
179
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);
187     }
188
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);
196     }
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);
203     }
204
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);
211     }
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);
217     }
218 }
219
220
221 /**
222  * Initialize any joysticks found.
223  */
224 int
225 fgJoystickInit() 
226 {
227     bool seen_joystick = false;
228
229     FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
230
231     setupDefaults();
232
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");
238             continue;
239         }
240 #ifdef WIN32
241         JOYCAPS jsCaps ;
242         joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
243         int nbuttons = jsCaps.wNumButtons;
244 #else
245         int nbuttons = MAX_BUTTONS;
246 #endif
247         
248         int naxes = js->getNumAxes();
249         joysticks[i].naxes = naxes;
250         joysticks[i].nbuttons = nbuttons;
251
252         FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
253         seen_joystick = true;
254
255         // Set up range arrays
256         float minRange[naxes];
257         float maxRange[naxes];
258         float center[naxes];
259
260         // Initialize with default values
261         js->getMinRange(minRange);
262         js->getMaxRange(maxRange);
263         js->getCenter(center);
264
265         // Allocate axes and buttons
266         joysticks[i].axes = new axis[naxes];
267         joysticks[i].buttons = new button[nbuttons];
268
269
270         //
271         // Initialize the axes.
272         //
273         for (int j = 0; j < naxes; j++) {
274             axis &a = joysticks[i].axes[j];
275
276             string base = "/input/";
277             base += jsNames[i];
278             base += '/';
279             base += axisNames[j];
280             FG_LOG(FG_INPUT, FG_INFO, "  Axis " << j << ':');
281
282             // Control property
283             string name = base;
284             name += "/control";
285             SGValue * value = current_properties.getValue(name);
286             if (value == 0) {
287                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
288                 continue;
289             }
290             const string &control = value->getStringValue();
291             a.value = current_properties.getValue(control, true);
292             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
293
294             // Dead band
295             name = base;
296             name += "/dead-band";
297             value = current_properties.getValue(name);
298             if (value != 0)
299                 js->setDeadBand(j, value->getDoubleValue());
300             FG_LOG(FG_INPUT, FG_INFO, "    dead-band is " << js->getDeadBand(j));
301
302             // Offset
303             name = base;
304             name += "/offset";
305             value = current_properties.getValue(name);
306             if (value != 0)
307                 a.offset = value->getDoubleValue();
308             FG_LOG(FG_INPUT, FG_INFO, "    offset is " << a.offset);
309
310
311             // Factor
312             name = base;
313             name += "/factor";
314             value = current_properties.getValue(name);
315             if (value != 0)
316                 a.factor = value->getDoubleValue();
317             FG_LOG(FG_INPUT, FG_INFO, "    factor is " << a.factor);
318
319
320             // Tolerance
321             name = base;
322             name += "/tolerance";
323             value = current_properties.getValue(name);
324             if (value != 0)
325                 a.tolerance = value->getDoubleValue();
326             FG_LOG(FG_INPUT, FG_INFO, "    tolerance is " << a.tolerance);
327
328
329             // Saturation
330             name = base;
331             name += "/saturation";
332             value = current_properties.getValue(name);
333             if (value != 0)
334                 js->setSaturation(j, value->getDoubleValue());
335             FG_LOG(FG_INPUT, FG_INFO, "    saturation is " << js->getSaturation(j));
336
337             // Minimum range
338             name = base;
339             name += "/min-range";
340             value = current_properties.getValue(name);
341             if (value != 0)
342                 minRange[j] = value->getDoubleValue();
343             FG_LOG(FG_INPUT, FG_INFO, "    min-range is " << minRange[j]);
344
345             // Maximum range
346             name = base;
347             name += "/max-range";
348             value = current_properties.getValue(name);
349             if (value != 0)
350                 maxRange[j] = value->getDoubleValue();
351             FG_LOG(FG_INPUT, FG_INFO, "    max-range is " << maxRange[j]);
352
353             // Center
354             name = base;
355             name += "/center";
356             value = current_properties.getValue(name);
357             if (value != 0)
358                 center[j] = value->getDoubleValue();
359             FG_LOG(FG_INPUT, FG_INFO, "    center is " << center[j]);
360         }
361
362
363         //
364         // Initialize the buttons.
365         //
366         for (int j = 0; j < nbuttons; j++) {
367             button &b = joysticks[i].buttons[j];
368       
369             string base = "/input/";
370             base += jsNames[i];
371             base += '/';
372             base += buttonNames[j];
373             FG_LOG(FG_INPUT, FG_INFO, "  Button " << j << ':');
374
375             // Control property
376             string name = base;
377             name += "/control";
378             cout << "Trying name " << name << endl;
379             SGValue * value = current_properties.getValue(name);
380             if (value == 0) {
381                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
382                 continue;
383             }
384             const string &control = value->getStringValue();
385             b.value = current_properties.getValue(control, true);
386             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
387
388             // Step
389             name = base;
390             name += "/step";
391             value = current_properties.getValue(name);
392             if (value != 0)
393                 b.step = value->getDoubleValue();
394             FG_LOG(FG_INPUT, FG_INFO, "    step is " << b.step);
395
396             // Type
397             name = base;
398             name += "/action";
399             value = current_properties.getValue(name);
400             string action = "adjust";
401             if (value != 0)
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;
412             } else {
413                 FG_LOG(FG_INPUT, FG_ALERT, "    unknown action " << action);
414                 action = "adjust";
415                 b.action = button::ADJUST;
416                 b.isRepeatable = true;
417             }
418             FG_LOG(FG_INPUT, FG_INFO, "    action is " << action);
419
420             // Repeatability.
421             name = base;
422             name += "/repeatable";
423             value = current_properties.getValue(name);
424             if (value != 0)
425                 b.isRepeatable = value->getBoolValue();
426             FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
427                                        "    repeatable" : "    not repeatable"));
428         }
429
430         js->setMinRange(minRange);
431         js->setMaxRange(maxRange);
432         js->setCenter(center);
433     }
434
435     if (seen_joystick)
436         FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
437     else
438         FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
439
440     return seen_joystick;
441 }
442
443
444 /**
445  * Update property values based on the joystick state(s).
446  */
447 int
448 fgJoystickRead()
449 {
450     int buttons;
451
452     for (int i = 0; i < MAX_JOYSTICKS; i++) {
453         jsJoystick * js = joysticks[i].js;
454         float axis_values[joysticks[i].naxes];
455         if (js->notWorking()) {
456             continue;
457         }
458
459         js->read(&buttons, axis_values);
460
461         //
462         // Axes
463         //
464         for (int j = 0; j < joysticks[i].naxes; j++) {
465             bool flag = true;
466             axis &a = joysticks[i].axes[j];
467
468             // If the axis hasn't changed, don't
469             // force the value.
470             if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
471                 continue;
472             else
473                 a.last_value = axis_values[j];
474
475             if (a.value)
476                 flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
477                                                a.factor);
478             if (!flag)
479                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
480                        << i << ", axis " << j);
481         }
482
483         //
484         // Buttons
485         //
486         for (int j = 0; j < joysticks[i].nbuttons; j++) {
487             bool flag;
488             button &b = joysticks[i].buttons[j];
489             if (b.value == 0)
490                 continue;
491
492             // Button is on.
493             if ((buttons & (1 << j)) > 0) {
494                 // Repeating?
495                 if (b.lastState == 1 && !b.isRepeatable)
496                     continue;
497
498                 switch (b.action) {
499                 case button::TOGGLE:
500                     if (b.step != 0.0) {
501                         if (b.value->getDoubleValue() == 0.0)
502                             flag = b.value->setDoubleValue(b.step);
503                         else
504                             flag = b.value->setDoubleValue(0.0);
505                     } else {
506                         if (b.value->getBoolValue())
507                             flag = b.value->setBoolValue(false);
508                         else
509                             flag = b.value->setBoolValue(true);
510                     }
511                     break;
512                 case button::SWITCH:
513                     flag = b.value->setDoubleValue(b.step);
514                     break;
515                 case button::ADJUST:
516                     if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
517                         FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
518                                << i << ", axis " << j);
519                     break;
520                 default:
521                     flag = false;
522                     break;
523                 }
524                 b.lastState = 1;
525
526                 // Button is off
527             } else {
528                 // Repeating?
529                 if (b.lastState == 0 && !b.isRepeatable)
530                     continue;
531
532                 switch (b.action) {
533                 case button::TOGGLE:
534                     // no op
535                     break;
536                 case button::SWITCH:
537                     flag = b.value->setDoubleValue(0.0);
538                     break;
539                 case button::ADJUST:
540                     // no op
541                     break;
542                 default:
543                     flag = false;
544                     break;
545                 }
546
547                 b.lastState = 0;
548             }
549             if (!flag)
550                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
551                        << jsNames[i] << ' ' << buttonNames[j]);
552         }
553     }
554
555     return true;
556 }
557
558 // end of joystick.cxx
559