Przeglądaj źródła

emscripten: Support analog gamepad triggers on both Firefox and Chrome.

Fixes #13051.
Ryan C. Gordon 9 godzin temu
rodzic
commit
614ae1b115

+ 1 - 1
src/joystick/SDL_gamepad_db.h

@@ -897,7 +897,7 @@ static const char *s_GamepadMappings[] = {
     "050000005e040000e0020000ff070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
 #endif
 #ifdef SDL_JOYSTICK_EMSCRIPTEN
-    "default,Standard Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+    "default,Standard Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
 #endif
 #ifdef SDL_JOYSTICK_PS2
     "0000000050533220436f6e74726f6c00,PS2 Controller,crc:ed87,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",

+ 51 - 18
src/joystick/emscripten/SDL_sysjoystick.c

@@ -66,36 +66,59 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep
         goto done;
     }
 
+    const int real_button_count = gamepadEvent->numButtons;
+    const int real_axis_count = gamepadEvent->numAxes;
+    int first_trigger_button = -1;
     int first_hat_button = -1;
     int num_buttons = gamepadEvent->numButtons;
-    if ((SDL_strcmp(gamepadEvent->mapping, "standard") == 0) && (num_buttons >= 16)) {  // maps to a game console gamepad layout, turn the d-pad into a hat.
-        num_buttons -= 4;
+    int num_axes = gamepadEvent->numAxes;
+    bool triggers_are_buttons = false;
+    if ((SDL_strcmp(gamepadEvent->mapping, "standard") == 0) && (num_buttons >= 16)) {  // maps to a game console gamepad layout, turn the d-pad into a hat, treat triggers as analog.
+        num_buttons -= 4;  // 4 dpad buttons become a hat.
         first_hat_button = 12;
+
+        if (num_axes == 4) {  // Chrome gives the triggers analog button values, Firefox exposes them as extra axes. Both have the digital buttons.
+            num_axes += 2;  // the two trigger "buttons"
+            triggers_are_buttons = true;
+        }
+
+        // dump the digital trigger buttons in any case.
+        first_trigger_button = 6;
+        num_buttons -= 2;
     }
 
     item->first_hat_button = first_hat_button;
+    item->first_trigger_button = first_trigger_button;
+    item->triggers_are_buttons = triggers_are_buttons;
     item->nhats = (first_hat_button >= 0) ? 1 : 0;
-    item->naxes = gamepadEvent->numAxes;
+    item->naxes = num_axes;
     item->nbuttons = num_buttons;
     item->device_instance = SDL_GetNextObjectID();
 
     item->timestamp = gamepadEvent->timestamp;
 
-    for (i = 0; i < item->naxes; i++) {
-        item->axis[i] = gamepadEvent->axis[i];
-    }
-
     int buttonidx = 0;
-    for (i = 0; i < item->nbuttons; i++, buttonidx++) {
+    for (i = 0; i < real_button_count; i++, buttonidx++) {
         if (buttonidx == first_hat_button) {
-            buttonidx += 3;  // skip these buttons, we're treating them as hat input.
+            buttonidx += 4;  // skip these buttons, we're treating them as hat input.
+        } else if (buttonidx == first_trigger_button) {
+            buttonidx += 2;  // skip these buttons, we're treating them as axes.
         }
         item->analogButton[i] = gamepadEvent->analogButton[buttonidx];
         item->digitalButton[i] = gamepadEvent->digitalButton[buttonidx];
     }
 
+    for (i = 0; i < real_axis_count; i++) {
+        item->axis[i] = gamepadEvent->axis[i];
+    }
+
+    if (item->triggers_are_buttons) {
+        item->axis[real_axis_count] = (gamepadEvent->analogButton[first_trigger_button] * 2.0f) - 1.0f;
+        item->axis[real_axis_count+1] = (gamepadEvent->analogButton[first_trigger_button+1] * 2.0f) - 1.0f;
+    }
+
     SDL_assert(item->nhats <= 1);  // there is (currently) only ever one of these, faked from the d-pad buttons.
-    if (item->nhats) {
+    if (first_hat_button != -1) {
         Uint8 value = SDL_HAT_CENTERED;
         // this currently expects the first button to be up, then down, then left, then right.
         if (gamepadEvent->digitalButton[first_hat_button + 0]) {
@@ -393,14 +416,19 @@ static void EMSCRIPTEN_JoystickUpdate(SDL_Joystick *joystick)
         if (result == EMSCRIPTEN_RESULT_SUCCESS) {
             if (gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
                 const int first_hat_button = item->first_hat_button;
+                const int first_trigger_button = item->first_trigger_button;
+                const int real_button_count = gamepadState.numButtons;
+                const int real_axis_count = gamepadState.numAxes;
 
                 int buttonidx = 0;
-                for (i = 0; i < item->nbuttons; i++, buttonidx++) {
+                for (i = 0; i < real_button_count; i++, buttonidx++) {
                     if (buttonidx == first_hat_button) {
                         buttonidx += 4;  // skip these buttons, we're treating them as hat input.
+                    } else if (buttonidx == first_trigger_button) {
+                        buttonidx += 2;  // skip these buttons, we're treating them as axes.
                     }
                     if (item->digitalButton[i] != gamepadState.digitalButton[buttonidx]) {
-                        bool down = (gamepadState.digitalButton[buttonidx] != 0);
+                        const bool down = (gamepadState.digitalButton[buttonidx] != 0);
                         SDL_SendJoystickButton(timestamp, item->joystick, i, down);
                     }
 
@@ -409,15 +437,20 @@ static void EMSCRIPTEN_JoystickUpdate(SDL_Joystick *joystick)
                     item->digitalButton[i] = gamepadState.digitalButton[buttonidx];
                 }
 
-                for (i = 0; i < item->naxes; i++) {
+                for (i = 0; i < real_axis_count; i++) {
                     if (item->axis[i] != gamepadState.axis[i]) {
-                        // do we need to do conversion?
-                        SDL_SendJoystickAxis(timestamp, item->joystick, i,
-                                                (Sint16)(32767. * gamepadState.axis[i]));
+                        SDL_SendJoystickAxis(timestamp, item->joystick, i, (Sint16)(32767.0f * gamepadState.axis[i]));
+                        item->axis[i] = gamepadState.axis[i];
                     }
+                }
 
-                    // store to compare in next update
-                    item->axis[i] = gamepadState.axis[i];
+                if (item->triggers_are_buttons) {
+                    for (i = 0; i < 2; i++) {
+                        if (item->axis[real_axis_count+i] != gamepadState.analogButton[first_trigger_button+i]) {
+                            SDL_SendJoystickAxis(timestamp, item->joystick, real_axis_count+i, (Sint16)(32767.0f * ((gamepadState.analogButton[first_trigger_button+i] * 2.0f) - 1.0f)));
+                            item->axis[real_axis_count+i] = gamepadState.analogButton[first_trigger_button+i];
+                        }
+                    }
                 }
 
                 SDL_assert(item->nhats <= 1);  // there is (currently) only ever one of these, faked from the d-pad buttons.

+ 3 - 1
src/joystick/emscripten/SDL_sysjoystick_c.h

@@ -35,11 +35,13 @@ typedef struct SDL_joylist_item
     SDL_JoystickID device_instance;
     SDL_Joystick *joystick;
     int first_hat_button;
+    int first_trigger_button;
+    bool triggers_are_buttons;
     int nhats;
     int nbuttons;
     int naxes;
     double timestamp;
-    double axis[64];
+    double axis[64];            // !!! FIXME: don't hardcode 64 on all of these.
     double analogButton[64];
     EM_BOOL digitalButton[64];
     Uint8 hat;  // there is (currently) only ever one of these, faked from the d-pad buttons.