shithub: choc

Download patch

ref: ba5470ce7422e54d87dfdd5b24c4e13bb58b095a
parent: a9be9d879ad141ba6f0153bbf7e823256495065b
author: Simon Howard <[email protected]>
date: Mon Apr 28 17:06:23 EDT 2014

setup: Add support for button axis calibration.

Detect when we need to configure a "button axis" by falling back to
buttons when none of the joystick axes are significantly outside of
the centered range. Add extra calibration stages to get the D-pad
buttons for right and down in these cases.

--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 
 #include "doomtype.h"
+#include "i_joystick.h"
 #include "m_config.h"
 #include "m_controls.h"
 #include "textscreen.h"
@@ -37,6 +38,10 @@
     CALIBRATE_CENTER,
     CALIBRATE_LEFT,
     CALIBRATE_UP,
+
+    // These are only used when defining button axes:
+    CALIBRATE_RIGHT,
+    CALIBRATE_DOWN,
 } calibration_stage_t;
 
 // SDL joystick successfully initialized?
@@ -51,6 +56,12 @@
 
 int joystick_index = -1;
 
+// Calibration button. This is the button the user pressed at the
+// start of the calibration sequence. They *must* press this button
+// for each subsequent sequence.
+
+static int calibrate_button = -1;
+
 // Which joystick axis to use for horizontal movement, and whether to
 // invert the direction:
 
@@ -185,20 +196,54 @@
             break;
         case CALIBRATE_UP:
             message = "Push the D-pad or joystick up,\n"
-                      "and press a button.";
+                      "and press the button.";
             break;
         case CALIBRATE_LEFT:
             message = "Push the D-pad or joystick to the\n"
-                      "left, and press a button.";
+                      "left, and press the button.";
             break;
+        case CALIBRATE_DOWN:
+            message = "Push the D-pad or joystick down,\n"
+                      "and press the button.";
+            break;
+        case CALIBRATE_RIGHT:
+            message = "Push the D-pad or joystick to the\n"
+                      "right, and press the button.";
+            break;
     }
 
     TXT_SetLabel(calibration_label, message);
 }
 
-static void CalibrateAxis(int *axis_index, int *axis_invert)
+// Search all axes on joystick being configured; find a button that is
+// pressed (other than the calibrate button).
+
+static int FindPressedAxisButton(void)
 {
     SDL_Joystick *joystick;
+    int i;
+
+    joystick = all_joysticks[joystick_index];
+
+    for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i)
+    {
+        if (i == calibrate_button)
+        {
+            continue;
+        }
+
+        if (SDL_JoystickGetButton(joystick, i))
+        {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+static boolean CalibrateAxis(int *axis_index, int *axis_invert)
+{
+    SDL_Joystick *joystick;
     int best_axis;
     int best_value;
     int best_invert;
@@ -218,7 +263,7 @@
     for (i=0; i<SDL_JoystickNumAxes(joystick); ++i)
     {
         axis_value = SDL_JoystickGetAxis(joystick, i);
-    
+
         if (abs(axis_value) > best_value)
         {
             best_value = abs(axis_value);
@@ -227,48 +272,153 @@
         }
     }
 
-    // Save the best values we have found
+    // Did we find one axis that had a significant value?
 
-    *axis_index = best_axis;
-    *axis_invert = best_invert;
+    if (best_value > 32768 / 4)
+    {
+        // Save the best values we have found
+
+        *axis_index = best_axis;
+        *axis_invert = best_invert;
+        return true;
+    }
+
+    // Otherwise, maybe this is a "button axis", like the PS3 SIXAXIS
+    // controller that exposes the D-pad as four individual buttons.
+    // Search for a button.
+
+    i = FindPressedAxisButton();
+
+    if (i >= 0)
+    {
+        *axis_index = CREATE_BUTTON_AXIS(i, 0);
+        *axis_invert = 0;
+        return true;
+    }
+
+    // User pressed the button without pushing the joystick anywhere.
+    return false;
 }
 
+static boolean SetButtonAxisPositive(int *axis_index)
+{
+    int button;
+
+    button = FindPressedAxisButton();
+
+    if (button >= 0)
+    {
+        *axis_index |= CREATE_BUTTON_AXIS(0, button);
+        return true;
+    }
+
+    return false;
+}
+
+static int NextCalibrateStage(void)
+{
+    switch (calibrate_stage)
+    {
+        case CALIBRATE_CENTER:
+            return CALIBRATE_LEFT;
+
+        // After pushing to the left, there are two possibilities:
+        // either it is a button axis, in which case we need to find
+        // the other button, or we can just move on to the next axis.
+        case CALIBRATE_LEFT:
+            if (IS_BUTTON_AXIS(joystick_x_axis))
+            {
+                return CALIBRATE_RIGHT;
+            }
+            else
+            {
+                return CALIBRATE_UP;
+            }
+
+        case CALIBRATE_RIGHT:
+            return CALIBRATE_UP;
+
+        case CALIBRATE_UP:
+            if (IS_BUTTON_AXIS(joystick_y_axis))
+            {
+                return CALIBRATE_DOWN;
+            }
+            else
+            {
+                // Finished.
+                return CALIBRATE_CENTER;
+            }
+
+        case CALIBRATE_DOWN:
+            // Finished.
+            return CALIBRATE_CENTER;
+    }
+}
+
 static int CalibrationEventCallback(SDL_Event *event, void *user_data)
 {
-    if (event->type == SDL_JOYBUTTONDOWN
-     && (joystick_index == -1 || event->jbutton.which == joystick_index))
+    boolean advance;
+
+    if (event->type != SDL_JOYBUTTONDOWN)
     {
+        return 0;
+    }
+
+    // At this point, we have a button press.
+    // In the first "center" stage, we're just trying to work out which
+    // joystick is being configured and which button the user is pressing.
+    if (calibrate_stage == CALIBRATE_CENTER)
+    {
+        joystick_index = event->jbutton.which;
+        calibrate_button = event->jbutton.button;
+
+        // Advance to next stage.
+        calibrate_stage = CALIBRATE_LEFT;
+        SetCalibrationLabel();
+        return 1;
+    }
+
+    // In subsequent stages, the user is asked to push in a specific
+    // direction and press the button. They must push the same button
+    // as they did before; this is necessary to support button axes.
+    if (event->jbutton.which == joystick_index
+     && event->jbutton.button == calibrate_button)
+    {
         switch (calibrate_stage)
         {
-            case CALIBRATE_CENTER:
-                // Centering stage selects which joystick to use.
-                joystick_index = event->jbutton.which;
+            default:
+            case CALIBRATE_LEFT:
+                advance = CalibrateAxis(&joystick_x_axis, &joystick_x_invert);
                 break;
 
-            case CALIBRATE_LEFT:
-                CalibrateAxis(&joystick_x_axis, &joystick_x_invert);
+            case CALIBRATE_RIGHT:
+                advance = SetButtonAxisPositive(&joystick_x_axis);
                 break;
 
             case CALIBRATE_UP:
-                CalibrateAxis(&joystick_y_axis, &joystick_y_invert);
+                advance = CalibrateAxis(&joystick_y_axis, &joystick_y_invert);
                 break;
+
+            case CALIBRATE_DOWN:
+                advance = SetButtonAxisPositive(&joystick_y_axis);
+                break;
         }
 
-        if (calibrate_stage == CALIBRATE_UP)
-        {
-            // Final stage; close the window
+        // Advance to the next calibration stage?
 
-            TXT_CloseWindow(calibration_window);
-        }
-        else
+        if (advance)
         {
-            // Advance to the next calibration stage
-
-            ++calibrate_stage;
+            calibrate_stage = NextCalibrateStage();
             SetCalibrationLabel();
-        }
 
-        return 1;
+            // Finished?
+            if (calibrate_stage == CALIBRATE_CENTER)
+            {
+                TXT_CloseWindow(calibration_window);
+            }
+
+            return 1;
+        }
     }
 
     return 0;