Commit 8e062f692578d3b32a516401aa732ea8fa516bc4

Sam Lantinga 2018-03-19T14:42:51

Generalized the handling of instantaneous guide button presses so there's a minimum of 100 ms between guide button press and release. This happens with at least the following controllers: All Apple MFI controllers, ASUS Gamepad, XiaoMi Bluetooth Controller

diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 3326c46..cdf97e4 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -25,6 +25,7 @@
 #include "SDL_events.h"
 #include "SDL_assert.h"
 #include "SDL_hints.h"
+#include "SDL_timer.h"
 #include "SDL_sysjoystick.h"
 #include "SDL_joystick_c.h"
 #include "SDL_gamecontrollerdb.h"
@@ -38,6 +39,9 @@
 #endif
 
 
+/* Many controllers turn the center button into an instantaneous button press */
+#define SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS   100
+
 #define SDL_CONTROLLER_PLATFORM_FIELD "platform:"
 
 /* a list of currently opened game controllers */
@@ -112,6 +116,7 @@ struct _SDL_GameController
     SDL_ExtendedGameControllerBind *bindings;
     SDL_ExtendedGameControllerBind **last_match_axis;
     Uint8 *last_hat_mask;
+    Uint32 guide_button_down;
 
     struct _SDL_GameController *next; /* pointer to next game controller we have allocated */
 };
@@ -1964,6 +1969,24 @@ SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameCon
     }
 #endif /* !SDL_EVENTS_DISABLED */
 
+    if (button == SDL_CONTROLLER_BUTTON_GUIDE) {
+        Uint32 now = SDL_GetTicks();
+        if (state == SDL_PRESSED) {
+            gamecontroller->guide_button_down = now;
+
+            if (gamecontroller->joystick->delayed_guide_button) {
+                /* Skip duplicate press */
+                return;
+            }
+        } else {
+            if (!SDL_TICKS_PASSED(now, gamecontroller->guide_button_down+SDL_MINIMUM_GUIDE_BUTTON_DELAY_MS) && !gamecontroller->joystick->force_recentering) {
+                gamecontroller->joystick->delayed_guide_button = SDL_TRUE;
+                return;
+            }
+            gamecontroller->joystick->delayed_guide_button = SDL_FALSE;
+        }
+    }
+
     /* translate the event, if desired */
     posted = 0;
 #if !SDL_EVENTS_DISABLED
@@ -2012,4 +2035,17 @@ SDL_GameControllerEventState(int state)
 #endif /* SDL_EVENTS_DISABLED */
 }
 
+void
+SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick)
+{
+    SDL_GameController *controllerlist = SDL_gamecontrollers;
+    while (controllerlist) {
+        if (controllerlist->joystick == joystick) {
+            SDL_PrivateGameControllerButton(controllerlist, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED);
+            break;
+        }
+        controllerlist = controllerlist->next;
+    }
+}
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h
index 6f75be6..5c8e768 100644
--- a/src/joystick/SDL_gamecontrollerdb.h
+++ b/src/joystick/SDL_gamecontrollerdb.h
@@ -208,7 +208,6 @@ static const char *s_ControllerMappings [] =
     "03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
 #endif
 #if defined(__ANDROID__)
-    "05000000050b000000450000bf7f3f00,ASUS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
     "050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
     "050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
     "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 38eef7c..ebbd2bf 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -874,6 +874,10 @@ SDL_JoystickUpdate(void)
     for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
         SDL_SYS_JoystickUpdate(joystick);
 
+        if (joystick->delayed_guide_button) {
+            SDL_GameControllerHandleDelayedGuideButton(joystick);
+        }
+
         if (joystick->force_recentering) {
             int i;
 
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index 0a8fdb4..2ce622e 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -42,6 +42,9 @@ extern SDL_bool SDL_IsGameControllerNameAndGUID(const char *name, SDL_JoystickGU
 /* Function to return whether a game controller should be ignored */
 extern SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid);
 
+/* Handle delayed guide button on a game controller */
+extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick);
+
 /* Internal event queueing functions */
 extern void SDL_PrivateJoystickAdded(int device_index);
 extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance);
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 0ce3115..efcc2d7 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -63,6 +63,7 @@ struct _SDL_Joystick
     int ref_count;              /* Reference count for multiple opens */
 
     SDL_bool is_game_controller;
+    SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */
     SDL_bool force_recentering; /* SDL_TRUE if this device needs to have its state reset to 0 */
     SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
     struct _SDL_Joystick *next; /* pointer to next joystick we have allocated */
diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m
index 71b81d3..2da1e6c 100644
--- a/src/joystick/iphoneos/SDL_sysjoystick.m
+++ b/src/joystick/iphoneos/SDL_sysjoystick.m
@@ -31,7 +31,6 @@
 #include "SDL_joystick.h"
 #include "SDL_hints.h"
 #include "SDL_stdinc.h"
-#include "SDL_timer.h"
 #include "../SDL_sysjoystick.h"
 #include "../SDL_joystick_c.h"
 #include "../steam/SDL_steamcontroller.h"
@@ -560,8 +559,6 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
         Uint8 hatstate = SDL_HAT_CENTERED;
         int i;
         int updateplayerindex = 0;
-        const Uint8 pausebutton = joystick->nbuttons - 1; /* The pause button is always last. */
-        const Uint32 PAUSE_RELEASE_DELAY_MS = 100;
 
         if (controller.extendedGamepad) {
             GCExtendedGamepad *gamepad = controller.extendedGamepad;
@@ -650,21 +647,13 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
         }
 
         for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
+            const Uint8 pausebutton = joystick->nbuttons - 1; /* The pause button is always last. */
             SDL_PrivateJoystickButton(joystick, pausebutton, SDL_PRESSED);
-            joystick->hwdata->pause_button_down_time = SDL_GetTicks();
-            if (!joystick->hwdata->pause_button_down_time) {
-                joystick->hwdata->pause_button_down_time = 1;
-            }
+            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
             updateplayerindex = YES;
         }
         joystick->hwdata->num_pause_presses = 0;
 
-        if (joystick->hwdata->pause_button_down_time &&
-            SDL_TICKS_PASSED(SDL_GetTicks(), joystick->hwdata->pause_button_down_time + PAUSE_RELEASE_DELAY_MS)) {
-            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
-            joystick->hwdata->pause_button_down_time = 0;
-        }
-
         if (updateplayerindex && controller.playerIndex == -1) {
             BOOL usedPlayerIndexSlots[4] = {NO, NO, NO, NO};