]> git.mxchange.org Git - flightgear.git/blob - src/Joystick/joystick.cxx
Changes from David Megginson in support of an xml based configuration file.
[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  * Initialize any joysticks found.
133  */
134 int
135 fgJoystickInit() 
136 {
137     bool seen_joystick = false;
138
139     FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
140
141     for (int i = 0; i < MAX_JOYSTICKS; i++) {
142         jsJoystick * js = new jsJoystick(i);
143         joysticks[i].js = js;
144         if (js->notWorking()) {
145             FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found");
146             continue;
147         }
148 #ifdef WIN32
149         JOYCAPS jsCaps ;
150         joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
151         int nbuttons = jsCaps.wNumButtons;
152 #else
153         int nbuttons = MAX_BUTTONS;
154 #endif
155         
156         int naxes = js->getNumAxes();
157         joysticks[i].naxes = naxes;
158         joysticks[i].nbuttons = nbuttons;
159
160         FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
161         seen_joystick = true;
162
163         // Set up range arrays
164         float *minRange = new float[naxes];
165         float *maxRange = new float[naxes];
166         float *center = new float[naxes];
167
168         // Initialize with default values
169         js->getMinRange(minRange);
170         js->getMaxRange(maxRange);
171         js->getCenter(center);
172
173         // Allocate axes and buttons
174         joysticks[i].axes = new axis[naxes];
175         joysticks[i].buttons = new button[nbuttons];
176
177
178         //
179         // Initialize the axes.
180         //
181         int j;
182         for (j = 0; j < naxes; j++) {
183             axis &a = joysticks[i].axes[j];
184
185             string base = "/input/";
186             base += jsNames[i];
187             base += '/';
188             base += axisNames[j];
189             FG_LOG(FG_INPUT, FG_INFO, "  Axis " << j << ':');
190
191             // Control property
192             string name = base;
193             name += "/control";
194             SGValue * value = current_properties.getValue(name);
195             if (value == 0) {
196                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
197                 continue;
198             }
199             const string &control = value->getStringValue();
200             a.value = current_properties.getValue(control, true);
201             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
202
203             // Dead band
204             name = base;
205             name += "/dead-band";
206             value = current_properties.getValue(name);
207             if (value != 0)
208                 js->setDeadBand(j, value->getDoubleValue());
209             FG_LOG(FG_INPUT, FG_INFO, "    dead-band is " << js->getDeadBand(j));
210
211             // Offset
212             name = base;
213             name += "/offset";
214             value = current_properties.getValue(name);
215             if (value != 0)
216                 a.offset = value->getDoubleValue();
217             FG_LOG(FG_INPUT, FG_INFO, "    offset is " << a.offset);
218
219
220             // Factor
221             name = base;
222             name += "/factor";
223             value = current_properties.getValue(name);
224             if (value != 0)
225                 a.factor = value->getDoubleValue();
226             FG_LOG(FG_INPUT, FG_INFO, "    factor is " << a.factor);
227
228
229             // Tolerance
230             name = base;
231             name += "/tolerance";
232             value = current_properties.getValue(name);
233             if (value != 0)
234                 a.tolerance = value->getDoubleValue();
235             FG_LOG(FG_INPUT, FG_INFO, "    tolerance is " << a.tolerance);
236
237
238             // Saturation
239             name = base;
240             name += "/saturation";
241             value = current_properties.getValue(name);
242             if (value != 0)
243                 js->setSaturation(j, value->getDoubleValue());
244             FG_LOG(FG_INPUT, FG_INFO, "    saturation is " << js->getSaturation(j));
245
246             // Minimum range
247             name = base;
248             name += "/min-range";
249             value = current_properties.getValue(name);
250             if (value != 0)
251                 minRange[j] = value->getDoubleValue();
252             FG_LOG(FG_INPUT, FG_INFO, "    min-range is " << minRange[j]);
253
254             // Maximum range
255             name = base;
256             name += "/max-range";
257             value = current_properties.getValue(name);
258             if (value != 0)
259                 maxRange[j] = value->getDoubleValue();
260             FG_LOG(FG_INPUT, FG_INFO, "    max-range is " << maxRange[j]);
261
262             // Center
263             name = base;
264             name += "/center";
265             value = current_properties.getValue(name);
266             if (value != 0)
267                 center[j] = value->getDoubleValue();
268             FG_LOG(FG_INPUT, FG_INFO, "    center is " << center[j]);
269         }
270
271
272         //
273         // Initialize the buttons.
274         //
275         for (j = 0; j < nbuttons; j++) {
276             button &b = joysticks[i].buttons[j];
277       
278             string base = "/input/";
279             base += jsNames[i];
280             base += '/';
281             base += buttonNames[j];
282             FG_LOG(FG_INPUT, FG_INFO, "  Button " << j << ':');
283
284             // Control property
285             string name = base;
286             name += "/control";
287             cout << "Trying name " << name << endl;
288             SGValue * value = current_properties.getValue(name);
289             if (value == 0) {
290                 FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
291                 continue;
292             }
293             const string &control = value->getStringValue();
294             b.value = current_properties.getValue(control, true);
295             FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
296
297             // Step
298             name = base;
299             name += "/step";
300             value = current_properties.getValue(name);
301             if (value != 0)
302                 b.step = value->getDoubleValue();
303             FG_LOG(FG_INPUT, FG_INFO, "    step is " << b.step);
304
305             // Type
306             name = base;
307             name += "/action";
308             value = current_properties.getValue(name);
309             string action = "adjust";
310             if (value != 0)
311                 action = value->getStringValue();
312             if (action == "toggle") {
313                 b.action = button::TOGGLE;
314                 b.isRepeatable = false;
315             } else if (action == "switch") {
316                 b.action = button::SWITCH;
317                 b.isRepeatable = false;
318             } else if (action == "adjust") {
319                 b.action = button::ADJUST;
320                 b.isRepeatable = true;
321             } else {
322                 FG_LOG(FG_INPUT, FG_ALERT, "    unknown action " << action);
323                 action = "adjust";
324                 b.action = button::ADJUST;
325                 b.isRepeatable = true;
326             }
327             FG_LOG(FG_INPUT, FG_INFO, "    action is " << action);
328
329             // Repeatability.
330             name = base;
331             name += "/repeatable";
332             value = current_properties.getValue(name);
333             if (value != 0)
334                 b.isRepeatable = value->getBoolValue();
335             FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
336                                        "    repeatable" : "    not repeatable"));
337         }
338
339         js->setMinRange(minRange);
340         js->setMaxRange(maxRange);
341         js->setCenter(center);
342
343         //-dw- clean up
344         delete minRange;
345         delete maxRange;
346         delete center;
347     }
348
349     if (seen_joystick)
350         FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
351     else
352         FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
353
354     return seen_joystick;
355 }
356
357
358 /**
359  * Update property values based on the joystick state(s).
360  */
361 int
362 fgJoystickRead()
363 {
364     int buttons;
365     float *axis_values = new float[MAX_AXES];
366
367     for (int i = 0; i < MAX_JOYSTICKS; i++) {
368         jsJoystick * js = joysticks[i].js;
369         // float *axis_values = new float[joysticks[i].naxes];
370         if (js->notWorking()) {
371             continue;
372         }
373
374         js->read(&buttons, axis_values);
375
376         //
377         // Axes
378         //
379         int j;
380         for (j = 0; j < joysticks[i].naxes; j++) {
381             bool flag = true;
382             axis &a = joysticks[i].axes[j];
383
384             // If the axis hasn't changed, don't
385             // force the value.
386             if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
387                 continue;
388             else
389                 a.last_value = axis_values[j];
390
391             if (a.value)
392                 flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
393                                                a.factor);
394             if (!flag)
395                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
396                        << i << ", axis " << j);
397         }
398
399         //
400         // Buttons
401         //
402         for (j = 0; j < joysticks[i].nbuttons; j++) {
403             bool flag = false;
404             button &b = joysticks[i].buttons[j];
405             if (b.value == 0)
406                 continue;
407
408             // Button is on.
409             if ((buttons & (1 << j)) > 0) {
410                 // Repeating?
411                 if (b.lastState == 1 && !b.isRepeatable)
412                     continue;
413
414                 switch (b.action) {
415                 case button::TOGGLE:
416                     if (b.step != 0.0) {
417                         if (b.value->getDoubleValue() == 0.0)
418                             flag = b.value->setDoubleValue(b.step);
419                         else
420                             flag = b.value->setDoubleValue(0.0);
421                     } else {
422                         if (b.value->getBoolValue())
423                             flag = b.value->setBoolValue(false);
424                         else
425                             flag = b.value->setBoolValue(true);
426                     }
427                     break;
428                 case button::SWITCH:
429                     flag = b.value->setDoubleValue(b.step);
430                     break;
431                 case button::ADJUST:
432                     if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
433                         FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
434                                << i << ", axis " << j);
435                     break;
436                 default:
437                     flag = false;
438                     break;
439                 }
440                 b.lastState = 1;
441
442                 // Button is off
443             } else {
444                 // Repeating?
445                 if (b.lastState == 0 && !b.isRepeatable)
446                     continue;
447
448                 switch (b.action) {
449                 case button::TOGGLE:
450                     // no op
451                     break;
452                 case button::SWITCH:
453                     flag = b.value->setDoubleValue(0.0);
454                     break;
455                 case button::ADJUST:
456                     // no op
457                     break;
458                 default:
459                     flag = false;
460                     break;
461                 }
462
463                 b.lastState = 0;
464             }
465             if (!flag)
466                 FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
467                        << jsNames[i] << ' ' << buttonNames[j]);
468         }
469     }
470
471     // -dw- cleanup 
472     delete axis_values;
473
474     return true;
475 }
476
477 // end of joystick.cxx