]> git.mxchange.org Git - flightgear.git/blob - src/Joystick/joystick.cxx
David Megginson's 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.setDoubleValue("/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.setDoubleValue("/input/js0/axis1/dead-band", 0.1);
142         props.setDoubleValue("/input/js0/axis1/factor", -1.0);
143     }
144
145     // Default axis 2 to rudder
146     if (!props.getValue("/input/js0/axis2/control")) {
147         props.setStringValue("/input/js0/axis2/control", "/controls/rudder");
148         props.setDoubleValue("/input/js0/axis2/dead-band", 0.1);
149     }
150
151     // Default axis 3 to throttle
152     // We need to fiddle with the offset
153     // and factor to make it work
154     if (!props.getValue("/input/js0/axis3/control")) {
155         props.setStringValue("/input/js0/axis3/control", "/controls/throttle");
156         props.setDoubleValue("/input/js0/axis3/dead-band", 0.0);
157         props.setDoubleValue("/input/js0/axis3/offset", -1.0);
158         props.setDoubleValue("/input/js0/axis3/factor", -0.5);
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/brakes/all");
165         props.setDoubleValue("/input/js0/button0/step", 1.0);
166         props.setDoubleValue("/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",
173                              "/controls/brakes/left");
174         props.setDoubleValue("/input/js0/button1/step", 1.0);
175         props.setDoubleValue("/input/js0/button1/repeatable", false);
176     }
177
178     // Default button 2 to right brake.
179     if (!props.getValue("/input/js0/button2/control")) {
180         props.setStringValue("/input/js0/button2/action", "switch");
181         props.setStringValue("/input/js0/button2/control",
182                              "/controls/brakes/right");
183         props.setDoubleValue("/input/js0/button2/step", 1.0);
184         props.setDoubleValue("/input/js0/button2/repeatable", false);
185     }
186
187     // Default buttons 3 and 4 to elevator trim
188     if (!props.getValue("/input/js0/button3/control")) {
189         props.setStringValue("/input/js0/button3/action", "adjust");
190         props.setStringValue("/input/js0/button3/control",
191                              "/controls/elevator-trim");
192         props.setDoubleValue("/input/js0/button3/step", 0.001);
193         props.setBoolValue("/input/js0/button3/repeatable", true);
194     }
195     if (!props.getValue("/input/js0/button4/control")) {
196         props.setStringValue("/input/js0/button4/action", "adjust");
197         props.setStringValue("/input/js0/button4/control",
198                              "/controls/elevator-trim");
199         props.setDoubleValue("/input/js0/button4/step", -0.001);
200         props.setBoolValue("/input/js0/button4/repeatable", true);
201     }
202
203     // Default buttons 5 and 6 to flaps
204     if (!props.getValue("/input/js0/button5/control")) {
205         props.setStringValue("/input/js0/button5/action", "adjust");
206         props.setStringValue("/input/js0/button5/control", "/controls/flaps");
207         props.setDoubleValue("/input/js0/button5/step", -0.34);
208         props.setBoolValue("/input/js0/button5/repeatable", false);
209     }
210     if (!props.getValue("/input/js0/button6/control")) {
211         props.setStringValue("/input/js0/button6/action", "adjust");
212         props.setStringValue("/input/js0/button6/control", "/controls/flaps");
213         props.setDoubleValue("/input/js0/button6/step", 0.34);
214         props.setBoolValue("/input/js0/button6/repeatable", false);
215     }
216 }
217
218
219 /**
220  * Initialize any joysticks found.
221  */
222 int
223 fgJoystickInit() 
224 {
225     bool seen_joystick = false;
226
227     FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
228
229     setupDefaults();
230
231     for (int i = 0; i < MAX_JOYSTICKS; i++) {
232         jsJoystick * js = new jsJoystick(i);
233         joysticks[i].js = js;
234         if (js->notWorking()) {
235             FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found");
236             continue;
237         }
238 #ifdef WIN32
239         JOYCAPS jsCaps ;
240         joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
241         int nbuttons = jsCaps.wNumButtons;
242 #else
243         int nbuttons = MAX_BUTTONS;
244 #endif
245         
246         int naxes = js->getNumAxes();
247         joysticks[i].naxes = naxes;
248         joysticks[i].nbuttons = nbuttons;
249
250         FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
251         seen_joystick = true;
252
253         // Set up range arrays
254         float minRange[naxes];
255         float maxRange[naxes];
256         float center[naxes];
257
258         // Initialize with default values
259         js->getMinRange(minRange);
260         js->getMaxRange(maxRange);
261         js->getCenter(center);
262
263         // Allocate axes and buttons
264         joysticks[i].axes = new axis[naxes];
265         joysticks[i].buttons = new button[nbuttons];
266
267
268         //
269         // Initialize the axes.
270         //
271         for (int j = 0; j < naxes; j++) {
272             axis &a = joysticks[i].axes[j];
273
274             string base = "/input/";
275             base += jsNames[i];
276             base += '/';
277             base += axisNames[j];
278             FG_LOG(FG_INPUT, FG_INFO, "  Axis " << j << ':');
279
280             // Control property
281             string name = base;
282             name += "/control";
283             SGValue * value = current_properties.getValue(name);
284             if (value == 0) {
285                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
286                 continue;
287             }
288             const string &control = value->getStringValue();
289             a.value = current_properties.getValue(control, true);
290             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
291
292             // Dead band
293             name = base;
294             name += "/dead-band";
295             value = current_properties.getValue(name);
296             if (value != 0)
297                 js->setDeadBand(j, value->getDoubleValue());
298             FG_LOG(FG_INPUT, FG_INFO, "    dead-band is " << js->getDeadBand(j));
299
300             // Offset
301             name = base;
302             name += "/offset";
303             value = current_properties.getValue(name);
304             if (value != 0)
305                 a.offset = value->getDoubleValue();
306             FG_LOG(FG_INPUT, FG_INFO, "    offset is " << a.offset);
307
308
309             // Factor
310             name = base;
311             name += "/factor";
312             value = current_properties.getValue(name);
313             if (value != 0)
314                 a.factor = value->getDoubleValue();
315             FG_LOG(FG_INPUT, FG_INFO, "    factor is " << a.factor);
316
317
318             // Tolerance
319             name = base;
320             name += "/tolerance";
321             value = current_properties.getValue(name);
322             if (value != 0)
323                 a.tolerance = value->getDoubleValue();
324             FG_LOG(FG_INPUT, FG_INFO, "    tolerance is " << a.tolerance);
325
326
327             // Saturation
328             name = base;
329             name += "/saturation";
330             value = current_properties.getValue(name);
331             if (value != 0)
332                 js->setSaturation(j, value->getDoubleValue());
333             FG_LOG(FG_INPUT, FG_INFO, "    saturation is " << js->getSaturation(j));
334
335             // Minimum range
336             name = base;
337             name += "/min-range";
338             value = current_properties.getValue(name);
339             if (value != 0)
340                 minRange[j] = value->getDoubleValue();
341             FG_LOG(FG_INPUT, FG_INFO, "    min-range is " << minRange[j]);
342
343             // Maximum range
344             name = base;
345             name += "/max-range";
346             value = current_properties.getValue(name);
347             if (value != 0)
348                 maxRange[j] = value->getDoubleValue();
349             FG_LOG(FG_INPUT, FG_INFO, "    max-range is " << maxRange[j]);
350
351             // Center
352             name = base;
353             name += "/center";
354             value = current_properties.getValue(name);
355             if (value != 0)
356                 center[j] = value->getDoubleValue();
357             FG_LOG(FG_INPUT, FG_INFO, "    center is " << center[j]);
358         }
359
360
361         //
362         // Initialize the buttons.
363         //
364         for (int j = 0; j < nbuttons; j++) {
365             button &b = joysticks[i].buttons[j];
366       
367             string base = "/input/";
368             base += jsNames[i];
369             base += '/';
370             base += buttonNames[j];
371             FG_LOG(FG_INPUT, FG_INFO, "  Button " << j << ':');
372
373             // Control property
374             string name = base;
375             name += "/control";
376             cout << "Trying name " << name << endl;
377             SGValue * value = current_properties.getValue(name);
378             if (value == 0) {
379                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
380                 continue;
381             }
382             const string &control = value->getStringValue();
383             b.value = current_properties.getValue(control, true);
384             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
385
386             // Step
387             name = base;
388             name += "/step";
389             value = current_properties.getValue(name);
390             if (value != 0)
391                 b.step = value->getDoubleValue();
392             FG_LOG(FG_INPUT, FG_INFO, "    step is " << b.step);
393
394             // Type
395             name = base;
396             name += "/action";
397             value = current_properties.getValue(name);
398             string action = "adjust";
399             if (value != 0)
400                 action = value->getStringValue();
401             if (action == "toggle") {
402                 b.action = button::TOGGLE;
403                 b.isRepeatable = false;
404             } else if (action == "switch") {
405                 b.action = button::SWITCH;
406                 b.isRepeatable = false;
407             } else if (action == "adjust") {
408                 b.action = button::ADJUST;
409                 b.isRepeatable = true;
410             } else {
411                 FG_LOG(FG_INPUT, FG_ALERT, "    unknown action " << action);
412                 action = "adjust";
413                 b.action = button::ADJUST;
414                 b.isRepeatable = true;
415             }
416             FG_LOG(FG_INPUT, FG_INFO, "    action is " << action);
417
418             // Repeatability.
419             name = base;
420             name += "/repeatable";
421             value = current_properties.getValue(name);
422             if (value != 0)
423                 b.isRepeatable = value->getBoolValue();
424             FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
425                                        "    repeatable" : "    not repeatable"));
426         }
427
428         js->setMinRange(minRange);
429         js->setMaxRange(maxRange);
430         js->setCenter(center);
431     }
432
433     if (seen_joystick)
434         FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
435     else
436         FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
437
438     return seen_joystick;
439 }
440
441
442 /**
443  * Update property values based on the joystick state(s).
444  */
445 int
446 fgJoystickRead()
447 {
448     int buttons;
449
450     for (int i = 0; i < MAX_JOYSTICKS; i++) {
451         jsJoystick * js = joysticks[i].js;
452         float axis_values[joysticks[i].naxes];
453         if (js->notWorking()) {
454             continue;
455         }
456
457         js->read(&buttons, axis_values);
458
459         //
460         // Axes
461         //
462         for (int j = 0; j < joysticks[i].naxes; j++) {
463             bool flag = true;
464             axis &a = joysticks[i].axes[j];
465
466             // If the axis hasn't changed, don't
467             // force the value.
468             if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
469                 continue;
470             else
471                 a.last_value = axis_values[j];
472
473             if (a.value)
474                 flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
475                                                a.factor);
476             if (!flag)
477                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
478                        << i << ", axis " << j);
479         }
480
481         //
482         // Buttons
483         //
484         for (int j = 0; j < joysticks[i].nbuttons; j++) {
485             bool flag;
486             button &b = joysticks[i].buttons[j];
487             if (b.value == 0)
488                 continue;
489
490             // Button is on.
491             if ((buttons & (1 << j)) > 0) {
492                 // Repeating?
493                 if (b.lastState == 1 && !b.isRepeatable)
494                     continue;
495
496                 switch (b.action) {
497                 case button::TOGGLE:
498                     if (b.step != 0.0) {
499                         if (b.value->getDoubleValue() == 0.0)
500                             flag = b.value->setDoubleValue(b.step);
501                         else
502                             flag = b.value->setDoubleValue(0.0);
503                     } else {
504                         if (b.value->getBoolValue())
505                             flag = b.value->setBoolValue(false);
506                         else
507                             flag = b.value->setBoolValue(true);
508                     }
509                     break;
510                 case button::SWITCH:
511                     flag = b.value->setDoubleValue(b.step);
512                     break;
513                 case button::ADJUST:
514                     if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
515                         FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
516                                << i << ", axis " << j);
517                     break;
518                 default:
519                     flag = false;
520                     break;
521                 }
522                 b.lastState = 1;
523
524                 // Button is off
525             } else {
526                 // Repeating?
527                 if (b.lastState == 0 && !b.isRepeatable)
528                     continue;
529
530                 switch (b.action) {
531                 case button::TOGGLE:
532                     // no op
533                     break;
534                 case button::SWITCH:
535                     flag = b.value->setDoubleValue(0.0);
536                     break;
537                 case button::ADJUST:
538                     // no op
539                     break;
540                 default:
541                     flag = false;
542                     break;
543                 }
544
545                 b.lastState = 0;
546             }
547             if (!flag)
548                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
549                        << jsNames[i] << ' ' << buttonNames[j]);
550         }
551     }
552
553     return true;
554 }
555
556 // end of joystick.cxx
557