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