Commit a0c4b56ff94d3ed11b7adb1ee6bbc4594210b4b6

Sam Lantinga 2015-09-30T15:39:30

SDL - added new SDL_JoystickCurrentPowerLevel() API that returns the battery level of the selected joystick. Currently only implemented for XInput devices, other platforms are a TODO. CR: Sam

diff --git a/include/SDL_joystick.h b/include/SDL_joystick.h
index cad06a8..a707e6c 100644
--- a/include/SDL_joystick.h
+++ b/include/SDL_joystick.h
@@ -71,6 +71,16 @@ typedef struct {
 
 typedef Sint32 SDL_JoystickID;
 
+typedef enum
+{
+    SDL_JOYSTICK_POWER_UNKNOWN = -1,
+    SDL_JOYSTICK_POWER_EMPTY,
+    SDL_JOYSTICK_POWER_LOW,
+    SDL_JOYSTICK_POWER_MEDIUM,
+    SDL_JOYSTICK_POWER_FULL,
+    SDL_JOYSTICK_POWER_WIRED,
+    SDL_JOYSTICK_POWER_MAX
+} SDL_JoystickPowerLevel;
 
 /* Function prototypes */
 /**
@@ -242,6 +252,11 @@ extern DECLSPEC Uint8 SDLCALL SDL_JoystickGetButton(SDL_Joystick * joystick,
  */
 extern DECLSPEC void SDLCALL SDL_JoystickClose(SDL_Joystick * joystick);
 
+/**
+*  Return the battery level of this joystick
+*/
+extern DECLSPEC SDL_JoystickPowerLevel SDLCALL SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick);
+
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
diff --git a/src/core/windows/SDL_xinput.c b/src/core/windows/SDL_xinput.c
index f892a8d..1e13fae 100644
--- a/src/core/windows/SDL_xinput.c
+++ b/src/core/windows/SDL_xinput.c
@@ -29,6 +29,7 @@
 XInputGetState_t SDL_XInputGetState = NULL;
 XInputSetState_t SDL_XInputSetState = NULL;
 XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
+XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation = NULL;
 DWORD SDL_XInputVersion = 0;
 
 static HANDLE s_pXInputDLL = 0;
@@ -55,6 +56,7 @@ WIN_LoadXInputDLL(void)
     SDL_XInputGetState = (XInputGetState_t)XInputGetState;
     SDL_XInputSetState = (XInputSetState_t)XInputSetState;
     SDL_XInputGetCapabilities = (XInputGetCapabilities_t)XInputGetCapabilities;
+    SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)XInputGetBatteryInformation;
 
     /* XInput 1.4 ships with Windows 8 and 8.1: */
     SDL_XInputVersion = (1 << 16) | 4;
@@ -108,6 +110,7 @@ WIN_LoadXInputDLL(void)
     }
     SDL_XInputSetState = (XInputSetState_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputSetState");
     SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress((HMODULE)s_pXInputDLL, "XInputGetCapabilities");
+    SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetBatteryInformation" );
     if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) {
         WIN_UnloadXInputDLL();
         return -1;
diff --git a/src/core/windows/SDL_xinput.h b/src/core/windows/SDL_xinput.h
index 5c57737..664edd0 100644
--- a/src/core/windows/SDL_xinput.h
+++ b/src/core/windows/SDL_xinput.h
@@ -76,6 +76,29 @@
 #define XINPUT_GAMEPAD_GUIDE 0x0400
 #endif
 
+#ifndef BATTERY_DEVTYPE_GAMEPAD
+#define BATTERY_DEVTYPE_GAMEPAD         0x00
+#endif
+#ifndef BATTERY_TYPE_WIRED
+#define BATTERY_TYPE_WIRED              0x01
+#endif
+
+#ifndef BATTERY_TYPE_UNKNOWN
+#define BATTERY_TYPE_UNKNOWN            0xFF
+#endif
+#ifndef BATTERY_LEVEL_EMPTY
+#define BATTERY_LEVEL_EMPTY             0x00
+#endif
+#ifndef BATTERY_LEVEL_LOW
+#define BATTERY_LEVEL_LOW               0x01
+#endif
+#ifndef BATTERY_LEVEL_MEDIUM
+#define BATTERY_LEVEL_MEDIUM            0x02
+#endif
+#ifndef BATTERY_LEVEL_FULL
+#define BATTERY_LEVEL_FULL              0x03
+#endif
+
 /* typedef's for XInput structs we use */
 typedef struct
 {
@@ -95,6 +118,12 @@ typedef struct
     XINPUT_GAMEPAD_EX Gamepad;
 } XINPUT_STATE_EX;
 
+typedef struct _XINPUT_BATTERY_INFORMATION
+{
+    BYTE BatteryType;
+    BYTE BatteryLevel;
+} XINPUT_BATTERY_INFORMATION, *PXINPUT_BATTERY_INFORMATION;
+
 /* Forward decl's for XInput API's we load dynamically and use if available */
 typedef DWORD (WINAPI *XInputGetState_t)
     (
@@ -115,17 +144,26 @@ typedef DWORD (WINAPI *XInputGetCapabilities_t)
     XINPUT_CAPABILITIES* pCapabilities  /* [out] Receives the capabilities */
     );
 
+typedef DWORD (WINAPI *XInputGetBatteryInformation_t)
+    (
+    _In_  DWORD                      dwUserIndex,
+    _In_  BYTE                       devType,
+    _Out_ XINPUT_BATTERY_INFORMATION *pBatteryInformation
+    );
+
 extern int WIN_LoadXInputDLL(void);
 extern void WIN_UnloadXInputDLL(void);
 
 extern XInputGetState_t SDL_XInputGetState;
 extern XInputSetState_t SDL_XInputSetState;
 extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
+extern XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation;
 extern DWORD SDL_XInputVersion;  /* ((major << 16) & 0xFF00) | (minor & 0xFF) */
 
 #define XINPUTGETSTATE          SDL_XInputGetState
 #define XINPUTSETSTATE          SDL_XInputSetState
 #define XINPUTGETCAPABILITIES   SDL_XInputGetCapabilities
+#define XINPUTGETBATTERYINFORMATION   SDL_XInputGetBatteryInformation
 
 #endif /* HAVE_XINPUT_H */
 
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 4725a57..532748c 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -178,6 +178,7 @@ SDL_JoystickOpen(int device_index)
     if (joystick->buttons) {
         SDL_memset(joystick->buttons, 0, joystick->nbuttons * sizeof(Uint8));
     }
+    joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
 
     /* Add joystick to list */
     ++joystick->ref_count;
@@ -619,10 +620,10 @@ SDL_PrivateJoystickButton(SDL_Joystick * joystick, Uint8 button, Uint8 state)
     /* Make sure we're not getting garbage or duplicate events */
     if (button >= joystick->nbuttons) {
         return 0;
-	}
-	if (state == joystick->buttons[button]) {
-		return 0;
-	}
+    }
+    if (state == joystick->buttons[button]) {
+        return 0;
+    }
 
     /* We ignore events if we don't have keyboard focus, except for button
      * release. */
@@ -825,4 +826,21 @@ SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
 }
 
 
+/* update the power level for this joystick */
+void SDL_PrivateJoystickBatteryLevel(SDL_Joystick * joystick, SDL_JoystickPowerLevel ePowerLevel)
+{
+    joystick->epowerlevel = ePowerLevel;
+}
+
+
+/* return its power level */
+SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
+{
+    if (!SDL_PrivateJoystickValid(joystick)) {
+        return (SDL_JOYSTICK_POWER_UNKNOWN);
+    }
+    return joystick->epowerlevel;
+}
+
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index ee4a14a..2da74bd 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -41,6 +41,8 @@ extern int SDL_PrivateJoystickHat(SDL_Joystick * joystick,
                                   Uint8 hat, Uint8 value);
 extern int SDL_PrivateJoystickButton(SDL_Joystick * joystick,
                                      Uint8 button, Uint8 state);
+extern void SDL_PrivateJoystickBatteryLevel( SDL_Joystick * joystick,
+    SDL_JoystickPowerLevel ePowerLevel );
 
 /* Internal sanity checking functions */
 extern int SDL_PrivateJoystickValid(SDL_Joystick * joystick);
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 7c104b5..f03e404 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -54,6 +54,7 @@ struct _SDL_Joystick
     int ref_count;              /* Reference count for multiple opens */
 
     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/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c
index b1c22ab..b3a4c9e 100644
--- a/src/joystick/windows/SDL_xinputjoystick.c
+++ b/src/joystick/windows/SDL_xinputjoystick.c
@@ -221,8 +221,39 @@ SDL_XINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickde
     return 0;
 }
 
+static void 
+UpdateXInputJoystickBatteryInformation(SDL_Joystick * joystick, XINPUT_BATTERY_INFORMATION *pBatteryInformation)
+{
+    if ( pBatteryInformation->BatteryType != BATTERY_TYPE_UNKNOWN )
+    {
+        SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN;
+        if (pBatteryInformation->BatteryType == BATTERY_TYPE_WIRED) {
+            ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
+        } else {
+            switch ( pBatteryInformation->BatteryLevel )
+            {
+            case BATTERY_LEVEL_EMPTY:
+                ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
+                break;
+            case BATTERY_LEVEL_LOW:
+                ePowerLevel = SDL_JOYSTICK_POWER_LOW;
+                break;
+            case BATTERY_LEVEL_MEDIUM:
+                ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
+                break;
+            default:
+            case BATTERY_LEVEL_FULL:
+                ePowerLevel = SDL_JOYSTICK_POWER_FULL;
+                break;
+            }
+        }
+
+        SDL_PrivateJoystickBatteryLevel( joystick, ePowerLevel );
+    }
+}
+
 static void
-UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
+UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION *pBatteryInformation)
 {
     static WORD s_XInputButtons[] = {
         XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
@@ -244,10 +275,12 @@ UpdateXInputJoystickState_OLD(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputS
     for (button = 0; button < SDL_arraysize(s_XInputButtons); ++button) {
         SDL_PrivateJoystickButton(joystick, button, (wButtons & s_XInputButtons[button]) ? SDL_PRESSED : SDL_RELEASED);
     }
+
+    UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation );
 }
 
 static void
-UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState)
+UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState, XINPUT_BATTERY_INFORMATION *pBatteryInformation)
 {
     static WORD s_XInputButtons[] = {
         XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
@@ -283,6 +316,8 @@ UpdateXInputJoystickState(SDL_Joystick * joystick, XINPUT_STATE_EX *pXInputState
         hat |= SDL_HAT_RIGHT;
     }
     SDL_PrivateJoystickHat(joystick, 0, hat);
+
+    UpdateXInputJoystickBatteryInformation( joystick, pBatteryInformation );
 }
 
 void
@@ -290,6 +325,7 @@ SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
 {
     HRESULT result;
     XINPUT_STATE_EX XInputState;
+    XINPUT_BATTERY_INFORMATION XBatteryInformation;
 
     if (!XINPUTGETSTATE)
         return;
@@ -301,12 +337,18 @@ SDL_XINPUT_JoystickUpdate(SDL_Joystick * joystick)
         return;
     }
 
+    SDL_zero( XBatteryInformation );
+    if ( XINPUTGETBATTERYINFORMATION )
+    {
+        result = XINPUTGETBATTERYINFORMATION( joystick->hwdata->userid, BATTERY_DEVTYPE_GAMEPAD, &XBatteryInformation );
+    }
+
     /* only fire events if the data changed from last time */
     if (XInputState.dwPacketNumber && XInputState.dwPacketNumber != joystick->hwdata->dwPacketNumber) {
         if (SDL_XInputUseOldJoystickMapping()) {
-            UpdateXInputJoystickState_OLD(joystick, &XInputState);
+            UpdateXInputJoystickState_OLD(joystick, &XInputState, &XBatteryInformation);
         } else {
-            UpdateXInputJoystickState(joystick, &XInputState);
+            UpdateXInputJoystickState(joystick, &XInputState, &XBatteryInformation);
         }
         joystick->hwdata->dwPacketNumber = XInputState.dwPacketNumber;
     }