foxygit / doom Log in
commit 7ad0f991beb544f57b00668f6bdfbd60331ae32f
Author:     Michael Day <contact@michaelcday.com>
AuthorDate: Wed Nov 29 16:08:15 2023 -0500
Commit:     Michael Day <contact@michaelcday.com>
CommitDate: Sun Dec 3 22:50:09 2023 -0500

    doom: Gamepad dpad or stick can navigate menu
---
 src/d_event.h     |   3 +-
 src/doom/m_menu.c |  23 ++++++--
 src/i_joystick.c  | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/i_joystick.h  |  18 +++++++
 4 files changed, 183 insertions(+), 16 deletions(-)

diff --git a/src/d_event.h b/src/d_event.h
index 7cc390a9..bc1050ae 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -60,6 +60,7 @@ typedef enum
     //    data3: Y axis mouse movement (forward/backward).
     //    data4: Third axis mouse movement (strafe).
     //    data5: Fourth axis mouse movement (look)
+    //    data6: Dpad and analog stick direction.
     ev_joystick,

     // Quit event. Triggered when the user clicks the "close" button
@@ -73,7 +74,7 @@ typedef struct
     evtype_t type;

     // Event-specific data; see the descriptions given above.
-    int data1, data2, data3, data4, data5;
+    int data1, data2, data3, data4, data5, data6;
 } event_t;


diff --git a/src/doom/m_menu.c b/src/doom/m_menu.c
index 073b8a12..5cb0274f 100644
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -30,6 +30,7 @@
 #include "deh_main.h"

 #include "i_input.h"
+#include "i_joystick.h"
 #include "i_swap.h"
 #include "i_system.h"
 #include "i_timer.h"
@@ -1357,6 +1358,7 @@ boolean M_Responder (event_t* ev)
     static  int     lasty = 0;
     static  int     mousex = 0;
     static  int     lastx = 0;
+    int dir;

     // In testcontrols mode, none of the function keys should do anything
     // - the only key is escape to quit.
@@ -1404,22 +1406,35 @@ boolean M_Responder (event_t* ev)

         if (menuactive)
         {
-            if (ev->data3 < 0)
+            if (JOY_GET_DPAD(ev->data6) != JOY_DIR_NONE)
+            {
+                dir = JOY_GET_DPAD(ev->data6);
+            }
+            else if (JOY_GET_LSTICK(ev->data6) != JOY_DIR_NONE)
+            {
+                dir = JOY_GET_LSTICK(ev->data6);
+            }
+            else
+            {
+                dir = JOY_GET_RSTICK(ev->data6);
+            }
+
+            if (dir & JOY_DIR_UP)
             {
                 key = key_menu_up;
                 joywait = I_GetTime() + 5;
             }
-            else if (ev->data3 > 0)
+            else if (dir & JOY_DIR_DOWN)
             {
                 key = key_menu_down;
                 joywait = I_GetTime() + 5;
             }
-            if (ev->data2 < 0)
+            if (dir & JOY_DIR_LEFT)
             {
                 key = key_menu_left;
                 joywait = I_GetTime() + 2;
             }
-            else if (ev->data2 > 0)
+            else if (dir & JOY_DIR_RIGHT)
             {
                 key = key_menu_right;
                 joywait = I_GetTime() + 2;
diff --git a/src/i_joystick.c b/src/i_joystick.c
index 575073bd..5ebaca7e 100644
--- a/src/i_joystick.c
+++ b/src/i_joystick.c
@@ -283,7 +283,8 @@ static int GetButtonsStateGamepad(void)

 // Read the state of an axis, inverting if necessary.

-static int GetAxisStateGamepad(int axis, int invert, int dead_zone)
+static int GetAxisStateGamepad(const int *axis_values, int axis, int invert,
+                               int dead_zone)
 {
     int result;

@@ -297,7 +298,7 @@ static int GetAxisStateGamepad(int axis, int invert, int dead_zone)
     // Dead zone is expressed as percentage of axis max value
     dead_zone = 32768 * dead_zone / 100;

-    result = SDL_GameControllerGetAxis(gamepad, axis);
+    result = axis_values[axis];

     if (result < dead_zone && result > -dead_zone)
     {
@@ -314,23 +315,130 @@ static int GetAxisStateGamepad(int axis, int invert, int dead_zone)
     return result;
 }

+#define DIRECTION_DEADZONE (50 * 32768 / 100)
+
+static int GetDirectionalInputGamepad(const int *axis_values)
+{
+    int value;
+    int dpad = JOY_DIR_NONE;
+    int leftstick = JOY_DIR_NONE;
+    int rightstick = JOY_DIR_NONE;
+
+    // Dpad.
+    if (SDL_GameControllerGetButton(gamepad, SDL_CONTROLLER_BUTTON_DPAD_UP))
+    {
+        dpad |=  JOY_DIR_UP;
+    }
+
+    if (SDL_GameControllerGetButton(gamepad, SDL_CONTROLLER_BUTTON_DPAD_DOWN))
+    {
+        dpad |=  JOY_DIR_DOWN;
+    }
+
+    if (SDL_GameControllerGetButton(gamepad, SDL_CONTROLLER_BUTTON_DPAD_LEFT))
+    {
+        dpad |=  JOY_DIR_LEFT;
+    }
+
+    if (SDL_GameControllerGetButton(gamepad, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
+    {
+        dpad |= JOY_DIR_RIGHT;
+    }
+
+    // Left stick.
+    value = axis_values[SDL_CONTROLLER_AXIS_LEFTY];
+
+    if (value > DIRECTION_DEADZONE || value < -DIRECTION_DEADZONE)
+    {
+        if (value > 0)
+        {
+            leftstick |= JOY_DIR_DOWN;
+        }
+        else
+        {
+            leftstick |= JOY_DIR_UP;
+        }
+    }
+
+    value = axis_values[SDL_CONTROLLER_AXIS_LEFTX];
+
+    if (value > DIRECTION_DEADZONE || value < -DIRECTION_DEADZONE)
+    {
+        if (value > 0)
+        {
+            leftstick |= JOY_DIR_RIGHT;
+        }
+        else
+        {
+            leftstick |= JOY_DIR_LEFT;
+        }
+    }
+
+    // Right stick.
+    value = axis_values[SDL_CONTROLLER_AXIS_RIGHTX];
+
+    if (value > DIRECTION_DEADZONE || value < -DIRECTION_DEADZONE)
+    {
+        if (value > 0)
+        {
+            rightstick |= JOY_DIR_RIGHT;
+        }
+        else
+        {
+            rightstick |= JOY_DIR_LEFT;
+        }
+    }
+
+    value = axis_values[SDL_CONTROLLER_AXIS_RIGHTY];
+
+    if (value > DIRECTION_DEADZONE || value < -DIRECTION_DEADZONE)
+    {
+        if (value > 0)
+        {
+            rightstick |= JOY_DIR_DOWN;
+        }
+        else
+        {
+            rightstick |= JOY_DIR_UP;
+        }
+    }
+
+    return ((dpad << DPAD_SHIFT) | (leftstick << LSTICK_SHIFT) |
+            (rightstick << RSTICK_SHIFT));
+}
+
 void I_UpdateGamepad(void)
 {
     if (gamepad != NULL)
     {
         event_t ev;
+        int axis_values[SDL_CONTROLLER_AXIS_MAX];
+
+        axis_values[SDL_CONTROLLER_AXIS_LEFTX] =
+            SDL_GameControllerGetAxis(gamepad, SDL_CONTROLLER_AXIS_LEFTX);
+
+        axis_values[SDL_CONTROLLER_AXIS_LEFTY] =
+            SDL_GameControllerGetAxis(gamepad, SDL_CONTROLLER_AXIS_LEFTY);
+
+        axis_values[SDL_CONTROLLER_AXIS_RIGHTX] =
+            SDL_GameControllerGetAxis(gamepad, SDL_CONTROLLER_AXIS_RIGHTX);
+
+        axis_values[SDL_CONTROLLER_AXIS_RIGHTY] =
+            SDL_GameControllerGetAxis(gamepad, SDL_CONTROLLER_AXIS_RIGHTY);

         ev.type = ev_joystick;
         ev.data1 = GetButtonsStateGamepad();
-        ev.data2 = GetAxisStateGamepad(joystick_x_axis, joystick_x_invert,
-                                       joystick_x_dead_zone);
-        ev.data3 = GetAxisStateGamepad(joystick_y_axis, joystick_y_invert,
-                                       joystick_y_dead_zone);
-        ev.data4 =
-            GetAxisStateGamepad(joystick_strafe_axis, joystick_strafe_invert,
-                                joystick_strafe_dead_zone);
-        ev.data5 = GetAxisStateGamepad(joystick_look_axis, joystick_look_invert,
+        ev.data2 = GetAxisStateGamepad(axis_values, joystick_x_axis,
+                                       joystick_x_invert, joystick_x_dead_zone);
+        ev.data3 = GetAxisStateGamepad(axis_values, joystick_y_axis,
+                                       joystick_y_invert, joystick_y_dead_zone);
+        ev.data4 = GetAxisStateGamepad(axis_values, joystick_strafe_axis,
+                                       joystick_strafe_invert,
+                                       joystick_strafe_dead_zone);
+        ev.data5 = GetAxisStateGamepad(axis_values, joystick_look_axis,
+                                       joystick_look_invert,
                                        joystick_look_dead_zone);
+        ev.data6 = GetDirectionalInputGamepad(axis_values);

         D_PostEvent(&ev);
     }
@@ -630,6 +738,31 @@ static int GetAxisState(int axis, int invert, int dead_zone)
     return result;
 }

+static int GetDirectionalInput(int x, int y)
+{
+    int retval = JOY_DIR_NONE;
+
+    if (x > 0)
+    {
+        retval |= JOY_DIR_RIGHT;
+    }
+    else if (x < 0)
+    {
+        retval |= JOY_DIR_LEFT;
+    }
+
+    if (y > 0)
+    {
+        retval |= JOY_DIR_DOWN;
+    }
+    else if (y < 0)
+    {
+        retval |= JOY_DIR_UP;
+    }
+
+    return (retval << DPAD_SHIFT);
+}
+
 void I_UpdateJoystick(void)
 {
     if (use_gamepad)
@@ -652,7 +785,7 @@ void I_UpdateJoystick(void)
                                 joystick_strafe_dead_zone);
         ev.data5 = GetAxisState(joystick_look_axis, joystick_look_invert,
                                 joystick_look_dead_zone);
-
+        ev.data6 = GetDirectionalInput(ev.data2, ev.data3);
         D_PostEvent(&ev);
     }
 }
diff --git a/src/i_joystick.h b/src/i_joystick.h
index 9831ee1c..a50c5309 100644
--- a/src/i_joystick.h
+++ b/src/i_joystick.h
@@ -74,6 +74,24 @@
 // To be used with SDL_JoystickGetGUIDString; see SDL_joystick.h
 #define GUID_STRING_BUF_SIZE 33

+// Helper macros for bitpacked directional data from gamepad inputs.
+#define DPAD_SHIFT 0
+#define LSTICK_SHIFT 4
+#define RSTICK_SHIFT 8
+#define JOY_GET_DPAD(x) (((x) >> DPAD_SHIFT) & 0xf)
+#define JOY_GET_LSTICK(x) (((x) >> LSTICK_SHIFT) & 0xf)
+#define JOY_GET_RSTICK(x) (((x) >> RSTICK_SHIFT) & 0xf)
+
+// 4-way direction data for gamepad directional inputs.
+enum
+{
+    JOY_DIR_NONE = 0x0,
+    JOY_DIR_UP = 0x1,
+    JOY_DIR_DOWN = 0x2,
+    JOY_DIR_LEFT = 0x4,
+    JOY_DIR_RIGHT = 0x8
+};
+
 // Extend the SDL_GameControllerButton enum to include the triggers.
 enum
 {