]> git.mxchange.org Git - flightgear.git/blobdiff - src/Joystick/joystick.cxx
David Megginson writes:
[flightgear.git] / src / Joystick / joystick.cxx
index 9ef133e4e248d850073f427c8a90281a8275f933..1aa232627726c0c7dbc549d7925821cb8b145fe1 100644 (file)
@@ -1,8 +1,9 @@
 // joystick.cxx -- joystick support
 //
-// Written by Curtis Olson, started October 1998.
+// Original module written by Curtis Olson, started October 1998.
+// Completely rewritten by David Megginson, July 2000.
 //
-// Copyright (C) 1998 - 1999  Curtis L. Olson - curt@flightgear.org
+// Copyright (C) 1998 - 2000  Curtis L. Olson - curt@flightgear.org
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -20,6 +21,7 @@
 //
 // $Id$
 
+#include <simgear/compiler.h>
 
 #ifdef HAVE_CONFIG_H
 #  include <config.h>
 #  include <windows.h>                     
 #endif
 
-#include <simgear/logstream.hxx>
+#include <math.h>
 
-#include <Aircraft/aircraft.hxx>
-#include <Main/options.hxx>
+#include <string>
 
-#if defined( ENABLE_PLIB_JOYSTICK )
-#  include <plib/js.h>         // plib include
-#elif defined( ENABLE_GLUT_JOYSTICK )
-#  include <GL/glut.h>
-#  include <simgear/xgl.h>
-#endif
+#include <Main/fg_props.hxx>
 
+#include <simgear/debug/logstream.hxx>
+#include <plib/js.h>
 
 #include "joystick.hxx"
 
+FG_USING_STD(string);
+FG_USING_STD(cout);
 
-#if defined( ENABLE_PLIB_JOYSTICK )
-
-// joystick classes
-static jsJoystick *js0;
-static jsJoystick *js1;
-
-// these will hold the values of the axes
-static float *js_ax0, *js_ax1;
-
-#elif defined( ENABLE_GLUT_JOYSTICK )
-
-// Do we want these user settable ??
-static float joy_scale = 1./1000;
-
-// play with following to get your desired sensitivity
-static int x_dead_zone = 50;
-static int y_dead_zone = 2*x_dead_zone;
-
-// Joystick support using glut -- William Riley -- riley@technologist.com
-
-// Joystick fixed values for calibration and scaling
-static float joy_x_max = joy_scale;
-static float joy_y_max = joy_scale;
-
-static int joy_z_min = 1000, /* joy_z_ctr=0, */ joy_z_max = -1000;
-static int joy_z_dead_min = 100, joy_z_dead_max = -100;
-
-#elif defined( MACOS )
-#  warning port me: no joystick support
+#ifdef WIN32
+static const int MAX_JOYSTICKS = 2;
 #else
-#  error port me: no joystick support
+static const int MAX_JOYSTICKS = 10;
+#endif
+static const int MAX_AXES = _JS_MAX_AXES;
+static const int MAX_BUTTONS = 32;
+
+
+/**
+ * Property names for joysticks and axes.
+ */
+static const char * jsNames[] = {
+    "js0", "js1", "js2", "js3", "js4",
+    "js5", "js6", "js7", "js8", "js9"
+};
+static const char * axisNames[] = {
+    "axis0", "axis1", "axis2", "axis3", "axis4",
+    "axis5", "axis6", "axis7", "axis8", "axis9"
+};
+static const char * buttonNames[] = {
+    "button0", "button1", "button2", "button3", "button4",
+    "button5", "button6", "button7", "button8", "button9",
+    "button10", "button11", "button12", "button13", "button14",
+    "button15", "button16", "button17", "button18", "button19",
+    "button20", "button21", "button22", "button23", "button24",
+    "button25", "button26", "button27", "button28", "button29",
+    "button30", "button31"
+};
+
+
+/**
+ * Settings for a single axis.
+ */
+struct axis {
+    axis () : value(0), offset(0.0), factor(1.0),
+       last_value(9999999), tolerance(0.002) {}
+    SGValue * value;
+    float offset;
+    float factor;
+    float last_value;
+    float tolerance;
+};
+
+
+/**
+ * Settings for a single button.
+ */
+struct button {
+    enum Action {
+       TOGGLE,
+       SWITCH,
+       ADJUST
+    };
+    button () : value(0), step(0.0), action(ADJUST), isRepeatable(true),
+       lastState(-1) {}
+    SGValue * value;
+    float step;
+    Action action;
+    bool isRepeatable;
+    int lastState;
+};
+
+
+/**
+ * Settings for a single joystick.
+ */
+struct joystick {
+    virtual ~joystick () {
+       delete js;
+#ifndef macintosh
+       delete axes;
+       delete buttons;
+#else
+       delete[] axes;
+       delete[] buttons;
 #endif
-
-
-
-#if defined( ENABLE_GLUT_JOYSTICK )
-
-// Function called by glutJoystickFunc(), adjusts read values and
-// passes them to the necessary aircraft control functions
-void joystick(unsigned int buttonMask, int js_x, int js_y, int js_z)
-{
-    float joy_x, joy_y, joy_z;
-    // adjust the values to fgfs's scale and allow a 'dead zone' to
-    // reduce jitter code adapted from joystick.c by Michele
-    // F. America - nomimarketing@mail.telepac.pt
-
-    if( js_x > -x_dead_zone && js_x < x_dead_zone) {
-       joy_x = 0.0;
-    } else {
-       joy_x = js_x * joy_scale;
-    }
-
-    if( js_y > -y_dead_zone && js_y < y_dead_zone) {
-       joy_y = 0.0;
-    } else {
-       joy_y = js_y * joy_scale;
-    }
-
-    if( js_z >= joy_z_dead_min && js_z <= joy_z_dead_max ) {
-       joy_z = 0.0;
     }
-    joy_z = (float)js_z / (float)(joy_z_max - joy_z_min);
-    joy_z = (((joy_z*2.0)+1.0)/2);
-
-    // Pass the values to the control routines
-    controls.set_elevator( -joy_y );
-    controls.set_aileron( joy_x );
-    controls.set_throttle( FGControls::ALL_ENGINES, joy_z );
-}
-
-#endif // ENABLE_GLUT_JOYSTICK
-
-
-// Initialize the joystick(s)
-int fgJoystickInit( void ) {
+    int naxes;
+    int nbuttons;
+    jsJoystick * js;
+    axis * axes;
+    button * buttons;
+};
 
-    FG_LOG( FG_INPUT, FG_INFO, "Initializing joystick" );
 
-#if defined( ENABLE_PLIB_JOYSTICK )
+/**
+ * Array of joystick settings.
+ */
+static joystick joysticks[MAX_JOYSTICKS];
 
-    js0 = new jsJoystick ( 0 );
-    js1 = new jsJoystick ( 1 );
 
-    if ( js0->notWorking () ) {
-       // not working
-    } else {
-       // allocate storage for axes values
-       js_ax0 = new float [ js0->getNumAxes() ];
-
-       // configure
-       js0->setDeadBand( 0, 0.1 );
-       js0->setDeadBand( 1, 0.1 );
+/**
+ * Initialize any joysticks found.
+ */
+int
+fgJoystickInit() 
+{
+    bool seen_joystick = false;
 
-       FG_LOG ( FG_INPUT, FG_INFO, 
-                "  Joystick 0 detected with " << js0->getNumAxes() 
-                << " axes" );
-    }
+    FG_LOG(FG_INPUT, FG_INFO, "Initializing joysticks");
 
-    if ( js1->notWorking () ) {
-       // not working
-    } else {
-       // allocate storage for axes values
-       js_ax1 = new float [ js1->getNumAxes() ];
+    for (int i = 0; i < MAX_JOYSTICKS; i++) {
+       jsJoystick * js = new jsJoystick(i);
+       joysticks[i].js = js;
+       if (js->notWorking()) {
+           FG_LOG(FG_INPUT, FG_INFO, "Joystick " << i << " not found");
+           continue;
+       }
+#ifdef WIN32
+       JOYCAPS jsCaps ;
+       joyGetDevCaps( i, &jsCaps, sizeof(jsCaps) );
+       int nbuttons = jsCaps.wNumButtons;
+#else
+       int nbuttons = MAX_BUTTONS;
+#endif
+       
+       int naxes = js->getNumAxes();
+       joysticks[i].naxes = naxes;
+       joysticks[i].nbuttons = nbuttons;
+
+       FG_LOG(FG_INPUT, FG_INFO, "Initializing joystick " << i);
+       seen_joystick = true;
+
+       // Set up range arrays
+       float *minRange = new float[naxes];
+       float *maxRange = new float[naxes];
+       float *center = new float[naxes];
+
+       // Initialize with default values
+       js->getMinRange(minRange);
+       js->getMaxRange(maxRange);
+       js->getCenter(center);
+
+       // Allocate axes and buttons
+       joysticks[i].axes = new axis[naxes];
+       joysticks[i].buttons = new button[nbuttons];
+
+
+       //
+       // Initialize the axes.
+       //
+       int j;
+       for (j = 0; j < naxes; j++) {
+           axis &a = joysticks[i].axes[j];
+
+           string base = "/input/";
+           base += jsNames[i];
+           base += '/';
+           base += axisNames[j];
+           FG_LOG(FG_INPUT, FG_INFO, "  Axis " << j << ':');
+
+           // Control property
+           string name = base;
+           name += "/control";
+           SGValue * value = fgGetValue(name);
+           if (value == 0) {
+               FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
+               continue;
+           }
+           const string &control = value->getStringValue();
+           a.value = fgGetValue(control, true);
+           FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
+
+           // Dead band
+           name = base;
+           name += "/dead-band";
+           value = fgGetValue(name);
+           if (value != 0)
+               js->setDeadBand(j, value->getDoubleValue());
+           FG_LOG(FG_INPUT, FG_INFO, "    dead-band is " << js->getDeadBand(j));
+
+           // Offset
+           name = base;
+           name += "/offset";
+           value = fgGetValue(name);
+           if (value != 0)
+               a.offset = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    offset is " << a.offset);
+
+
+           // Factor
+           name = base;
+           name += "/factor";
+           value = fgGetValue(name);
+           if (value != 0)
+               a.factor = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    factor is " << a.factor);
+
+
+           // Tolerance
+           name = base;
+           name += "/tolerance";
+           value = fgGetValue(name);
+           if (value != 0)
+               a.tolerance = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    tolerance is " << a.tolerance);
+
+
+           // Saturation
+           name = base;
+           name += "/saturation";
+           value = fgGetValue(name);
+           if (value != 0)
+               js->setSaturation(j, value->getDoubleValue());
+           FG_LOG(FG_INPUT, FG_INFO, "    saturation is " << js->getSaturation(j));
+
+           // Minimum range
+           name = base;
+           name += "/min-range";
+           value = fgGetValue(name);
+           if (value != 0)
+               minRange[j] = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    min-range is " << minRange[j]);
+
+           // Maximum range
+           name = base;
+           name += "/max-range";
+           value = fgGetValue(name);
+           if (value != 0)
+               maxRange[j] = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    max-range is " << maxRange[j]);
+
+           // Center
+           name = base;
+           name += "/center";
+           value = fgGetValue(name);
+           if (value != 0)
+               center[j] = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    center is " << center[j]);
+       }
 
-       // configure
-       js1->setDeadBand( 0, 0.1 );
-       js1->setDeadBand( 1, 0.1 );
 
-       FG_LOG ( FG_INPUT, FG_INFO,
-                "  Joystick 1 detected with " << js1->getNumAxes() 
-                << " axes" );
-    }
+       //
+       // Initialize the buttons.
+       //
+       for (j = 0; j < nbuttons; j++) {
+           button &b = joysticks[i].buttons[j];
+      
+           string base = "/input/";
+           base += jsNames[i];
+           base += '/';
+           base += buttonNames[j];
+           FG_LOG(FG_INPUT, FG_INFO, "  Button " << j << ':');
+
+           // Control property
+           string name = base;
+           name += "/control";
+           cout << "Trying name " << name << endl;
+           SGValue * value = fgGetValue(name);
+           if (value == 0) {
+               FG_LOG(FG_INPUT, FG_INFO, "    no control defined");
+               continue;
+           }
+           const string &control = value->getStringValue();
+           b.value = fgGetValue(control, true);
+           FG_LOG(FG_INPUT, FG_INFO, "    using control " << control);
+
+           // Step
+           name = base;
+           name += "/step";
+           value = fgGetValue(name);
+           if (value != 0)
+               b.step = value->getDoubleValue();
+           FG_LOG(FG_INPUT, FG_INFO, "    step is " << b.step);
+
+           // Type
+           name = base;
+           name += "/action";
+           value = fgGetValue(name);
+           string action = "adjust";
+           if (value != 0)
+               action = value->getStringValue();
+           if (action == "toggle") {
+               b.action = button::TOGGLE;
+               b.isRepeatable = false;
+           } else if (action == "switch") {
+               b.action = button::SWITCH;
+               b.isRepeatable = false;
+           } else if (action == "adjust") {
+               b.action = button::ADJUST;
+               b.isRepeatable = true;
+           } else {
+               FG_LOG(FG_INPUT, FG_ALERT, "    unknown action " << action);
+               action = "adjust";
+               b.action = button::ADJUST;
+               b.isRepeatable = true;
+           }
+           FG_LOG(FG_INPUT, FG_INFO, "    action is " << action);
+
+           // Repeatability.
+           name = base;
+           name += "/repeatable";
+           value = fgGetValue(name);
+           if (value != 0)
+               b.isRepeatable = value->getBoolValue();
+           FG_LOG(FG_INPUT, FG_INFO, (b.isRepeatable ?
+                                      "    repeatable" : "    not repeatable"));
+       }
 
-    if ( js0->notWorking() && js1->notWorking() ) {
-       FG_LOG ( FG_INPUT, FG_INFO, "  No joysticks detected" );
-       return 0;
-    }
+       js->setMinRange(minRange);
+       js->setMaxRange(maxRange);
+       js->setCenter(center);
 
-    // I hate doing this sort of thing, but it's overridable from the
-    // command line/config file.  If the user hasn't specified an
-    // autocoordination preference, and if they have a single 2 axis
-    // joystick, then automatical enable auto_coordination.
-
-    if ( (current_options.get_auto_coordination() == 
-         fgOPTIONS::FG_AUTO_COORD_NOT_SPECIFIED) &&
-        (!js0->notWorking() && js1->notWorking() && (js0->getNumAxes() < 3)
-         )
-        )
-    {
-       current_options.set_auto_coordination(fgOPTIONS::FG_AUTO_COORD_ENABLED);
+       //-dw- clean up
+       delete minRange;
+       delete maxRange;
+       delete center;
     }
 
+    if (seen_joystick)
+       FG_LOG(FG_INPUT, FG_INFO, "Done initializing joysticks");
+    else
+       FG_LOG(FG_INPUT, FG_ALERT, "No joysticks detected");
 
-#elif defined( ENABLE_GLUT_JOYSTICK )
-
-    glutJoystickFunc(joystick, 100);
-
-#elif defined( MACOS )
-#  warning port me: no joystick support
-#else
-#  error port me: no joystick support
-#endif
-
-    return 1;
+    return seen_joystick;
 }
 
 
-#if defined( ENABLE_PLIB_JOYSTICK )
-
-// update the control parameters based on joystick intput
-int fgJoystickRead( void ) {
-    int b;
+/**
+ * Update property values based on the joystick state(s).
+ */
+int
+fgJoystickRead()
+{
+    int buttons;
+    float *axis_values = new float[MAX_AXES];
+
+    for (int i = 0; i < MAX_JOYSTICKS; i++) {
+       jsJoystick * js = joysticks[i].js;
+       // float *axis_values = new float[joysticks[i].naxes];
+       if (js->notWorking()) {
+           continue;
+       }
 
-    if ( ! js0->notWorking() ) {
-       js0->read( &b, js_ax0 ) ;
-       controls.set_aileron( js_ax0[0] );
-       controls.set_elevator( -js_ax0[1] );
+       js->read(&buttons, axis_values);
+
+       //
+       // Axes
+       //
+       int j;
+       for (j = 0; j < joysticks[i].naxes; j++) {
+           bool flag = true;
+           axis &a = joysticks[i].axes[j];
+
+           // If the axis hasn't changed, don't
+           // force the value.
+           if (fabs(axis_values[j] - a.last_value) <= a.tolerance)
+               continue;
+           else
+               a.last_value = axis_values[j];
+
+           if (a.value)
+               flag = a.value->setDoubleValue((axis_values[j] + a.offset) *
+                                              a.factor);
+           if (!flag)
+               FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
+                      << i << ", axis " << j);
+       }
 
-       //  Added by William Riley -- riley@technologist.com
-       if ( js0->getNumAxes() >= 3 ) {
-           controls.set_throttle( FGControls::ALL_ENGINES,
-                                  ((-js_ax0[2] + 1) / 2) );
-       } 
-       if ( js0->getNumAxes() > 3 ) {
-           if ( current_options.get_auto_coordination() !=
-                 fgOPTIONS::FG_AUTO_COORD_ENABLED ) 
-           {
-               controls.set_rudder( js_ax0[3] );
+       //
+       // Buttons
+       //
+       for (j = 0; j < joysticks[i].nbuttons; j++) {
+           bool flag = false;
+           button &b = joysticks[i].buttons[j];
+           if (b.value == 0)
+               continue;
+
+           // Button is on.
+           if ((buttons & (1 << j)) > 0) {
+               // Repeating?
+               if (b.lastState == 1 && !b.isRepeatable)
+                   continue;
+
+               switch (b.action) {
+               case button::TOGGLE:
+                   if (b.step != 0.0) {
+                       if (b.value->getDoubleValue() == 0.0)
+                           flag = b.value->setDoubleValue(b.step);
+                       else
+                           flag = b.value->setDoubleValue(0.0);
+                   } else {
+                       if (b.value->getBoolValue())
+                           flag = b.value->setBoolValue(false);
+                       else
+                           flag = b.value->setBoolValue(true);
+                   }
+                   break;
+               case button::SWITCH:
+                   flag = b.value->setDoubleValue(b.step);
+                   break;
+               case button::ADJUST:
+                   if (!b.value->setDoubleValue(b.value->getDoubleValue() + b.step))
+                       FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for joystick "
+                              << i << ", axis " << j);
+                   break;
+               default:
+                   flag = false;
+                   break;
+               }
+               b.lastState = 1;
+
+               // Button is off
+           } else {
+               // Repeating?
+               if (b.lastState == 0 && !b.isRepeatable)
+                   continue;
+
+               switch (b.action) {
+               case button::ADJUST:
+               case button::TOGGLE:
+                   // no op
+                   flag = true;
+                   break;
+               case button::SWITCH:
+                   flag = b.value->setDoubleValue(0.0);
+                   break;
+               default:
+                   flag = false;
+                   break;
+               }
+
+               b.lastState = 0;
            }
+           if (!flag)
+               FG_LOG(FG_INPUT, FG_ALERT, "Failed to set value for "
+                      << jsNames[i] << ' ' << buttonNames[j]);
        }
-       //  End of William's code
-
     }
 
-    if ( ! js1->notWorking() ) {
-       js1->read( &b, js_ax1 ) ;
-       if ( current_options.get_auto_coordination() !=
-            fgOPTIONS::FG_AUTO_COORD_ENABLED ) 
-       {
-           controls.set_rudder( js_ax1[0] );
-       }
-       controls.set_throttle( FGControls::ALL_ENGINES, -js_ax1[1] * 1.05 );
-    }
+    // -dw- cleanup 
+    delete axis_values;
 
-    return 1;
+    return true;
 }
 
-#endif // ENABLE_PLIB_JOYSTICK
-
+// end of joystick.cxx