Added a Windows Gaming Input joystick driver
authorSam Lantinga <slouken@libsdl.org>
Sat, 18 Apr 2020 21:41:37 -0700
changeset 137685e7c6765ceda
parent 13767 671d8b2548b3
child 13769 e93582363cb1
Added a Windows Gaming Input joystick driver

This driver supports the Razer Atrox Arcade Stick

Some of the quirks of this driver, inherent in Windows Gaming Input:
* There will never appear to be controllers connected at startup. You must support hot-plugging in order to see these controllers.
* You can't read the state of the guide button
* You can't get controller events in the background
VisualC/SDL/SDL.vcxproj
VisualC/SDL/SDL.vcxproj.filters
include/SDL_config_windows.h
src/joystick/SDL_gamecontroller.c
src/joystick/SDL_gamecontrollerdb.h
src/joystick/SDL_joystick.c
src/joystick/SDL_joystick_c.h
src/joystick/SDL_sysjoystick.h
src/joystick/windows/SDL_windows_gaming_input.c
src/sensor/windows/SDL_windowssensor.c
test/controllermap.c
test/testgamecontroller.c
test/testjoystick.c
     1.1 --- a/VisualC/SDL/SDL.vcxproj	Fri Apr 17 21:30:58 2020 -0700
     1.2 +++ b/VisualC/SDL/SDL.vcxproj	Sat Apr 18 21:41:37 2020 -0700
     1.3 @@ -418,11 +418,13 @@
     1.4      <ClCompile Include="..\..\src\events\SDL_windowevents.c" />
     1.5      <ClCompile Include="..\..\src\file\SDL_rwops.c" />
     1.6      <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
     1.7 +    <ClCompile Include="..\..\src\haptic\dummy\SDL_syshaptic.c" />
     1.8      <ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
     1.9      <ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
    1.10      <ClCompile Include="..\..\src\haptic\windows\SDL_windowshaptic.c" />
    1.11      <ClCompile Include="..\..\src\haptic\windows\SDL_xinputhaptic.c" />
    1.12      <ClCompile Include="..\..\src\hidapi\windows\hid.c" />
    1.13 +    <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
    1.14      <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapijoystick.c" />
    1.15      <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_gamecube.c" />
    1.16      <ClCompile Include="..\..\src\joystick\hidapi\SDL_hidapi_ps4.c" />
    1.17 @@ -438,6 +440,7 @@
    1.18      <ClCompile Include="..\..\src\joystick\windows\SDL_mmjoystick.c" />
    1.19      <ClCompile Include="..\..\src\joystick\windows\SDL_rawinputjoystick.c" />
    1.20      <ClCompile Include="..\..\src\joystick\windows\SDL_windowsjoystick.c" />
    1.21 +    <ClCompile Include="..\..\src\joystick\windows\SDL_windows_gaming_input.c" />
    1.22      <ClCompile Include="..\..\src\joystick\windows\SDL_xinputjoystick.c" />
    1.23      <ClCompile Include="..\..\src\libm\e_atan2.c" />
    1.24      <ClCompile Include="..\..\src\libm\e_exp.c" />
     2.1 --- a/VisualC/SDL/SDL.vcxproj.filters	Fri Apr 17 21:30:58 2020 -0700
     2.2 +++ b/VisualC/SDL/SDL.vcxproj.filters	Sat Apr 18 21:41:37 2020 -0700
     2.3 @@ -484,6 +484,9 @@
     2.4      <ClCompile Include="..\..\src\video\yuv2rgb\yuv_rgb.c" />
     2.5      <ClCompile Include="..\..\src\joystick\windows\SDL_rawinputjoystick.c" />
     2.6      <ClCompile Include="..\..\src\sensor\windows\SDL_windowssensor.c" />
     2.7 +    <ClCompile Include="..\..\src\haptic\dummy\SDL_syshaptic.c" />
     2.8 +    <ClCompile Include="..\..\src\joystick\dummy\SDL_sysjoystick.c" />
     2.9 +    <ClCompile Include="..\..\src\joystick\windows\SDL_windows_gaming_input.c" />
    2.10    </ItemGroup>
    2.11    <ItemGroup>
    2.12      <ResourceCompile Include="..\..\src\main\windows\version.rc" />
     3.1 --- a/include/SDL_config_windows.h	Fri Apr 17 21:30:58 2020 -0700
     3.2 +++ b/include/SDL_config_windows.h	Sat Apr 18 21:41:37 2020 -0700
     3.3 @@ -198,6 +198,7 @@
     3.4  #define SDL_JOYSTICK_HIDAPI 1
     3.5  #define SDL_JOYSTICK_RAWINPUT   1
     3.6  #define SDL_JOYSTICK_VIRTUAL    1
     3.7 +/*#define SDL_JOYSTICK_WGI    1*/
     3.8  #define SDL_JOYSTICK_XINPUT 1
     3.9  #define SDL_HAPTIC_DINPUT   1
    3.10  #define SDL_HAPTIC_XINPUT   1
    3.11 @@ -260,3 +261,5 @@
    3.12  #endif
    3.13  
    3.14  #endif /* SDL_config_windows_h_ */
    3.15 +
    3.16 +/* vi: set ts=4 sw=4 expandtab: */
     4.1 --- a/src/joystick/SDL_gamecontroller.c	Fri Apr 17 21:30:58 2020 -0700
     4.2 +++ b/src/joystick/SDL_gamecontroller.c	Sat Apr 18 21:41:37 2020 -0700
     4.3 @@ -1045,7 +1045,7 @@
     4.4      }
     4.5  #endif /* __LINUX__ */
     4.6  
     4.7 -    if (!mapping && name) {
     4.8 +    if (!mapping && name && !SDL_IsJoystickWGI(guid)) {
     4.9          if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX")) {
    4.10              mapping = s_pXInputMapping;
    4.11          }
     5.1 --- a/src/joystick/SDL_gamecontrollerdb.h	Fri Apr 17 21:30:58 2020 -0700
     5.2 +++ b/src/joystick/SDL_gamecontrollerdb.h	Sat Apr 18 21:41:37 2020 -0700
     5.3 @@ -34,6 +34,9 @@
     5.4  #if SDL_JOYSTICK_XINPUT
     5.5      "xinput,XInput Controller,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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
     5.6  #endif
     5.7 +#if SDL_JOYSTICK_WGI
     5.8 +    "0300000032150000000a000000007703,Razer Atrox Arcade Stick,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,dpup:b10,dpdown:b12,dpleft:b13,dpright:b11,lefttrigger:b8,righttrigger:b9,",
     5.9 +#endif
    5.10  #if SDL_JOYSTICK_DINPUT
    5.11      "03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,",
    5.12      "03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
     6.1 --- a/src/joystick/SDL_joystick.c	Fri Apr 17 21:30:58 2020 -0700
     6.2 +++ b/src/joystick/SDL_joystick.c	Sat Apr 18 21:41:37 2020 -0700
     6.3 @@ -58,6 +58,9 @@
     6.4  #ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */
     6.5      &SDL_HIDAPI_JoystickDriver,
     6.6  #endif
     6.7 +#if defined(SDL_JOYSTICK_WGI)
     6.8 +    &SDL_WGI_JoystickDriver,
     6.9 +#endif
    6.10  #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
    6.11      &SDL_WINDOWS_JoystickDriver,
    6.12  #endif
    6.13 @@ -1732,6 +1735,12 @@
    6.14  }
    6.15  
    6.16  SDL_bool
    6.17 +SDL_IsJoystickWGI(SDL_JoystickGUID guid)
    6.18 +{
    6.19 +    return (guid.data[14] == 'w') ? SDL_TRUE : SDL_FALSE;
    6.20 +}
    6.21 +
    6.22 +SDL_bool
    6.23  SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid)
    6.24  {
    6.25      return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE;
    6.26 @@ -1840,6 +1849,10 @@
    6.27          }
    6.28      }
    6.29  
    6.30 +    if (SDL_IsJoystickWGI(guid)) {
    6.31 +        return (SDL_JoystickType)guid.data[15];
    6.32 +    }
    6.33 +
    6.34      if (SDL_IsJoystickVirtual(guid)) {
    6.35          return (SDL_JoystickType)guid.data[15];
    6.36      }
     7.1 --- a/src/joystick/SDL_joystick_c.h	Fri Apr 17 21:30:58 2020 -0700
     7.2 +++ b/src/joystick/SDL_joystick_c.h	Sat Apr 18 21:41:37 2020 -0700
     7.3 @@ -70,6 +70,9 @@
     7.4  /* Function to return whether a joystick guid comes from the XInput driver */
     7.5  extern SDL_bool SDL_IsJoystickXInput(SDL_JoystickGUID guid);
     7.6  
     7.7 +/* Function to return whether a joystick guid comes from the WGI driver */
     7.8 +extern SDL_bool SDL_IsJoystickWGI(SDL_JoystickGUID guid);
     7.9 +
    7.10  /* Function to return whether a joystick guid comes from the HIDAPI driver */
    7.11  extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid);
    7.12  
     8.1 --- a/src/joystick/SDL_sysjoystick.h	Fri Apr 17 21:30:58 2020 -0700
     8.2 +++ b/src/joystick/SDL_sysjoystick.h	Sat Apr 18 21:41:37 2020 -0700
     8.3 @@ -153,6 +153,7 @@
     8.4  extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
     8.5  extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
     8.6  extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
     8.7 +extern SDL_JoystickDriver SDL_WGI_JoystickDriver;
     8.8  extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver;
     8.9  
    8.10  #endif /* SDL_sysjoystick_h_ */
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/joystick/windows/SDL_windows_gaming_input.c	Sat Apr 18 21:41:37 2020 -0700
     9.3 @@ -0,0 +1,705 @@
     9.4 +/*
     9.5 +  Simple DirectMedia Layer
     9.6 +  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
     9.7 +
     9.8 +  This software is provided 'as-is', without any express or implied
     9.9 +  warranty.  In no event will the authors be held liable for any damages
    9.10 +  arising from the use of this software.
    9.11 +
    9.12 +  Permission is granted to anyone to use this software for any purpose,
    9.13 +  including commercial applications, and to alter it and redistribute it
    9.14 +  freely, subject to the following restrictions:
    9.15 +
    9.16 +  1. The origin of this software must not be misrepresented; you must not
    9.17 +     claim that you wrote the original software. If you use this software
    9.18 +     in a product, an acknowledgment in the product documentation would be
    9.19 +     appreciated but is not required.
    9.20 +  2. Altered source versions must be plainly marked as such, and must not be
    9.21 +     misrepresented as being the original software.
    9.22 +  3. This notice may not be removed or altered from any source distribution.
    9.23 +*/
    9.24 +#include "../../SDL_internal.h"
    9.25 +
    9.26 +#ifdef SDL_JOYSTICK_WGI
    9.27 +
    9.28 +#include "SDL_endian.h"
    9.29 +#include "SDL_events.h"
    9.30 +#include "../SDL_sysjoystick.h"
    9.31 +
    9.32 +#include "../../core/windows/SDL_windows.h"
    9.33 +#define COBJMACROS
    9.34 +#include "windows.gaming.input.h"
    9.35 +
    9.36 +
    9.37 +struct joystick_hwdata
    9.38 +{
    9.39 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller;
    9.40 +    __x_ABI_CWindows_CGaming_CInput_CIGameController *gamecontroller;
    9.41 +    __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo *battery;
    9.42 +    __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
    9.43 +    UINT64 timestamp;
    9.44 +};
    9.45 +
    9.46 +typedef struct WindowsGamingInputControllerState {
    9.47 +    SDL_JoystickID instance_id;
    9.48 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller;
    9.49 +    char *name;
    9.50 +    SDL_JoystickGUID guid;
    9.51 +    SDL_JoystickType type;
    9.52 +    int naxes;
    9.53 +    int nhats;
    9.54 +    int nbuttons;
    9.55 +} WindowsGamingInputControllerState;
    9.56 +
    9.57 +static struct {
    9.58 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics *statics;
    9.59 +    __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics *arcade_stick_statics;
    9.60 +    __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2 *arcade_stick_statics2;
    9.61 +    __x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics *flight_stick_statics;
    9.62 +    __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
    9.63 +    __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *gamepad_statics2;
    9.64 +    __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics *racing_wheel_statics;
    9.65 +    __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2 *racing_wheel_statics2;
    9.66 +    EventRegistrationToken controller_added_token;
    9.67 +    EventRegistrationToken controller_removed_token;
    9.68 +    int controller_count;
    9.69 +    WindowsGamingInputControllerState *controllers;
    9.70 +} wgi;
    9.71 +
    9.72 +static const IID IID_IRawGameControllerStatics = { 0xEB8D0792, 0xE95A, 0x4B19, { 0xAF, 0xC7, 0x0A, 0x59, 0xF8, 0xBF, 0x75, 0x9E } };
    9.73 +static const IID IID_IRawGameController = { 0x7CAD6D91, 0xA7E1, 0x4F71, { 0x9A, 0x78, 0x33, 0xE9, 0xC5, 0xDF, 0xEA, 0x62 } };
    9.74 +static const IID IID_IRawGameController2 = { 0x43C0C035, 0xBB73, 0x4756, { 0xA7, 0x87, 0x3E, 0xD6, 0xBE, 0xA6, 0x17, 0xBD } };
    9.75 +static const IID IID_IEventHandler_RawGameController = { 0x00621c22, 0x42e8, 0x529f, { 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72 } };
    9.76 +static const IID IID_IGameController = { 0x1BAF6522, 0x5F64, 0x42C5, { 0x82, 0x67, 0xB9, 0xFE, 0x22, 0x15, 0xBF, 0xBD } };
    9.77 +static const IID IID_IGameControllerBatteryInfo = { 0xDCECC681, 0x3963, 0x4DA6, { 0x95, 0x5D, 0x55, 0x3F, 0x3B, 0x6F, 0x61, 0x61 } };
    9.78 +static const IID IID_IArcadeStickStatics = { 0x5C37B8C8, 0x37B1, 0x4AD8, { 0x94, 0x58, 0x20, 0x0F, 0x1A, 0x30, 0x01, 0x8E } };
    9.79 +static const IID IID_IArcadeStickStatics2 = { 0x52B5D744, 0xBB86, 0x445A, { 0xB5, 0x9C, 0x59, 0x6F, 0x0E, 0x2A, 0x49, 0xDF } };
    9.80 +static const IID IID_IArcadeStick = { 0xB14A539D, 0xBEFB, 0x4C81, { 0x80, 0x51, 0x15, 0xEC, 0xF3, 0xB1, 0x30, 0x36 } };
    9.81 +static const IID IID_IFlightStickStatics = { 0x5514924A, 0xFECC, 0x435E, { 0x83, 0xDC, 0x5C, 0xEC, 0x8A, 0x18, 0xA5, 0x20 } };
    9.82 +static const IID IID_IFlightStick = { 0xB4A2C01C, 0xB83B, 0x4459, { 0xA1, 0xA9, 0x97, 0xB0, 0x3C, 0x33, 0xDA, 0x7C } };
    9.83 +static const IID IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
    9.84 +static const IID IID_IGamepadStatics2 = { 0x42676DC5, 0x0856, 0x47C4, { 0x92, 0x13, 0xB3, 0x95, 0x50, 0x4C, 0x3A, 0x3C } };
    9.85 +static const IID IID_IGamepad = { 0xBC7BB43C, 0x0A69, 0x3903, { 0x9E, 0x9D, 0xA5, 0x0F, 0x86, 0xA4, 0x5D, 0xE5 } };
    9.86 +static const IID IID_IRacingWheelStatics = { 0x3AC12CD5, 0x581B, 0x4936, { 0x9F, 0x94, 0x69, 0xF1, 0xE6, 0x51, 0x4C, 0x7D } };
    9.87 +static const IID IID_IRacingWheelStatics2 = { 0xE666BCAA, 0xEDFD, 0x4323, { 0xA9, 0xF6, 0x3C, 0x38, 0x40, 0x48, 0xD1, 0xED } };
    9.88 +static const IID IID_IRacingWheel = { 0xF546656F, 0xE106, 0x4C82, { 0xA9, 0x0F, 0x55, 0x40, 0x12, 0x90, 0x4B, 0x85 } };
    9.89 +
    9.90 +extern SDL_bool SDL_XINPUT_Enabled(void);
    9.91 +
    9.92 +static SDL_bool
    9.93 +SDL_IsXInputDevice(Uint16 vendor, Uint16 product)
    9.94 +{
    9.95 +    PRAWINPUTDEVICELIST raw_devices = NULL;
    9.96 +    UINT i, raw_device_count = 0;
    9.97 +    LONG vidpid = MAKELONG(vendor, product);
    9.98 +
    9.99 +    if (!SDL_XINPUT_Enabled()) {
   9.100 +        return SDL_FALSE;
   9.101 +    }
   9.102 +
   9.103 +    /* Go through RAWINPUT (WinXP and later) to find HID devices. */
   9.104 +    if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) {
   9.105 +        return SDL_FALSE;  /* oh well. */
   9.106 +    }
   9.107 +
   9.108 +    raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count);
   9.109 +    if (raw_devices == NULL) {
   9.110 +        SDL_OutOfMemory();
   9.111 +        return SDL_FALSE;
   9.112 +    }
   9.113 +
   9.114 +    if (GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) {
   9.115 +        SDL_free(raw_devices);
   9.116 +        raw_devices = NULL;
   9.117 +        return SDL_FALSE;  /* oh well. */
   9.118 +    }
   9.119 +
   9.120 +    for (i = 0; i < raw_device_count; i++) {
   9.121 +        RID_DEVICE_INFO rdi;
   9.122 +        char devName[128];
   9.123 +        UINT rdiSize = sizeof(rdi);
   9.124 +        UINT nameSize = SDL_arraysize(devName);
   9.125 +
   9.126 +        rdi.cbSize = sizeof(rdi);
   9.127 +        if ((raw_devices[i].dwType == RIM_TYPEHID) &&
   9.128 +            (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) &&
   9.129 +            (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) &&
   9.130 +            (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) &&
   9.131 +            (SDL_strstr(devName, "IG_") != NULL)) {
   9.132 +            return SDL_TRUE;
   9.133 +        }
   9.134 +    }
   9.135 +
   9.136 +    return SDL_FALSE;
   9.137 +}
   9.138 +
   9.139 +static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This, REFIID riid, void **ppvObject)
   9.140 +{
   9.141 +    if (!ppvObject) {
   9.142 +        return E_INVALIDARG;
   9.143 +    }
   9.144 +
   9.145 +    *ppvObject = NULL;
   9.146 +    if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IEventHandler_RawGameController)) {
   9.147 +        *ppvObject = This;
   9.148 +        return S_OK;
   9.149 +    }
   9.150 +    return E_NOINTERFACE;
   9.151 +}
   9.152 +
   9.153 +static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This)
   9.154 +{
   9.155 +    return 1;
   9.156 +}
   9.157 +
   9.158 +static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This)
   9.159 +{
   9.160 +    return 1;
   9.161 +}
   9.162 +
   9.163 +static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController * *e)
   9.164 +{
   9.165 +    HRESULT hr;
   9.166 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;
   9.167 +
   9.168 +    hr = IUnknown_QueryInterface((IUnknown *)e, &IID_IRawGameController, (void **)&controller);
   9.169 +    if (SUCCEEDED(hr)) {
   9.170 +        char *name = NULL;
   9.171 +        SDL_JoystickGUID guid;
   9.172 +        Uint16 vendor = 0;
   9.173 +        Uint16 product = 0;
   9.174 +        Uint16 version = 0;
   9.175 +        SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN;
   9.176 +        Uint16 *guid16 = (Uint16 *)guid.data;
   9.177 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;
   9.178 +        __x_ABI_CWindows_CGaming_CInput_CIGameController *gamecontroller = NULL;
   9.179 +
   9.180 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor);
   9.181 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product);
   9.182 +
   9.183 +        if (SDL_IsXInputDevice(vendor, product)) {
   9.184 +            /* This will be handled by the XInput driver */
   9.185 +            __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
   9.186 +            return S_OK;
   9.187 +        }
   9.188 +
   9.189 +        hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID_IRawGameController2, (void **)&controller2);
   9.190 +        if (SUCCEEDED(hr)) {
   9.191 +            HSTRING hString;
   9.192 +
   9.193 +            hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString);
   9.194 +            if (SUCCEEDED(hr)) {
   9.195 +                HMODULE hModule = LoadLibraryA("combase.dll");
   9.196 +                if (hModule != NULL) {
   9.197 +                    typedef PCWSTR (WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length);
   9.198 +
   9.199 +                    WindowsGetStringRawBuffer_t WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)GetProcAddress(hModule, "WindowsGetStringRawBuffer");
   9.200 +                    if (WindowsGetStringRawBufferFunc) {
   9.201 +                        PCWSTR string = WindowsGetStringRawBufferFunc(hString, NULL);
   9.202 +                        if (string) {
   9.203 +                            name = WIN_StringToUTF8(string);
   9.204 +                        }
   9.205 +                    }
   9.206 +                    FreeLibrary(hModule);
   9.207 +                }
   9.208 +            }
   9.209 +            __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);
   9.210 +        }
   9.211 +
   9.212 +        if (SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID_IGameController, (void **)&gamecontroller))) {
   9.213 +            __x_ABI_CWindows_CGaming_CInput_CIArcadeStick *arcade_stick = NULL;
   9.214 +            __x_ABI_CWindows_CGaming_CInput_CIFlightStick *flight_stick = NULL;
   9.215 +            __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad = NULL;
   9.216 +            __x_ABI_CWindows_CGaming_CInput_CIRacingWheel *racing_wheel = NULL;
   9.217 +
   9.218 +            if (wgi.gamepad_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, gamecontroller, &gamepad)) && gamepad) {
   9.219 +                type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
   9.220 +                __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
   9.221 +            } else if (wgi.arcade_stick_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_FromGameController(wgi.arcade_stick_statics2, gamecontroller, &arcade_stick)) && arcade_stick) {
   9.222 +                type = SDL_JOYSTICK_TYPE_ARCADE_STICK;
   9.223 +                __x_ABI_CWindows_CGaming_CInput_CIArcadeStick_Release(arcade_stick);
   9.224 +            } else if (wgi.flight_stick_statics && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_FromGameController(wgi.flight_stick_statics, gamecontroller, &flight_stick)) && flight_stick) {
   9.225 +                type = SDL_JOYSTICK_TYPE_FLIGHT_STICK;
   9.226 +                __x_ABI_CWindows_CGaming_CInput_CIFlightStick_Release(flight_stick);
   9.227 +            } else if (wgi.racing_wheel_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_FromGameController(wgi.racing_wheel_statics2, gamecontroller, &racing_wheel)) && racing_wheel) {
   9.228 +                type = SDL_JOYSTICK_TYPE_WHEEL;
   9.229 +                __x_ABI_CWindows_CGaming_CInput_CIRacingWheel_Release(racing_wheel);
   9.230 +            }
   9.231 +            __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(gamecontroller);
   9.232 +        }
   9.233 +
   9.234 +        /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
   9.235 +        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
   9.236 +        *guid16++ = 0;
   9.237 +        *guid16++ = SDL_SwapLE16(vendor);
   9.238 +        *guid16++ = 0;
   9.239 +        *guid16++ = SDL_SwapLE16(product);
   9.240 +        *guid16++ = 0;
   9.241 +        *guid16++ = SDL_SwapLE16(version);
   9.242 +        *guid16++ = 0;
   9.243 +
   9.244 +        /* Note that this is a Windows Gaming Input device for special handling elsewhere */
   9.245 +        guid.data[14] = 'w';
   9.246 +        guid.data[15] = (Uint8)type;
   9.247 +
   9.248 +        if (SDL_ShouldIgnoreJoystick(name, guid)) {
   9.249 +            SDL_free(name);
   9.250 +        } else {
   9.251 +            /* New device, add it */
   9.252 +            WindowsGamingInputControllerState *controllers = SDL_realloc(wgi.controllers, sizeof(wgi.controllers[0]) * (wgi.controller_count + 1));
   9.253 +            if (controllers) {
   9.254 +                WindowsGamingInputControllerState *state = &controllers[wgi.controller_count];
   9.255 +                SDL_JoystickID joystickID = SDL_GetNextJoystickInstanceID();
   9.256 +
   9.257 +                SDL_zerop(state);
   9.258 +                state->instance_id = joystickID;
   9.259 +                state->controller = controller;
   9.260 +                state->name = name;
   9.261 +                state->guid = guid;
   9.262 +                state->type = type;
   9.263 +
   9.264 +                __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(controller, &state->nbuttons);
   9.265 +                __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(controller, &state->naxes);
   9.266 +                __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(controller, &state->nhats);
   9.267 +
   9.268 +                __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(controller);
   9.269 +
   9.270 +                ++wgi.controller_count;
   9.271 +                wgi.controllers = controllers;
   9.272 +
   9.273 +                SDL_PrivateJoystickAdded(joystickID);
   9.274 +            }
   9.275 +        }
   9.276 +
   9.277 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
   9.278 +    }
   9.279 +    return S_OK;
   9.280 +}
   9.281 +
   9.282 +static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController * *e)
   9.283 +{
   9.284 +    HRESULT hr;
   9.285 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;
   9.286 +
   9.287 +    hr = IUnknown_QueryInterface((IUnknown *)e, &IID_IRawGameController, (void **)&controller);
   9.288 +    if (SUCCEEDED(hr)) {
   9.289 +        int i;
   9.290 +
   9.291 +        for (i = 0; i < wgi.controller_count ; i++) {
   9.292 +            if (wgi.controllers[i].controller == controller) {
   9.293 +                WindowsGamingInputControllerState *state = &wgi.controllers[i];
   9.294 +                SDL_JoystickID joystickID = state->instance_id;
   9.295 +
   9.296 +                __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(state->controller);
   9.297 +
   9.298 +                SDL_free(state->name);
   9.299 +
   9.300 +                --wgi.controller_count;
   9.301 +                if (i < wgi.controller_count) {
   9.302 +                    SDL_memmove(&wgi.controllers[i], &wgi.controllers[i + 1], (wgi.controller_count - i) * sizeof(wgi.controllers[i]));
   9.303 +                }
   9.304 +
   9.305 +                SDL_PrivateJoystickRemoved(joystickID);
   9.306 +                break;
   9.307 +            }
   9.308 +        }
   9.309 +
   9.310 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
   9.311 +    }
   9.312 +    return S_OK;
   9.313 +}
   9.314 +
   9.315 +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = {
   9.316 +    IEventHandler_CRawGameControllerVtbl_QueryInterface,
   9.317 +    IEventHandler_CRawGameControllerVtbl_AddRef,
   9.318 +    IEventHandler_CRawGameControllerVtbl_Release,
   9.319 +    IEventHandler_CRawGameControllerVtbl_InvokeAdded
   9.320 +};
   9.321 +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController controller_added = {
   9.322 +    &controller_added_vtbl
   9.323 +};
   9.324 +
   9.325 +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = {
   9.326 +    IEventHandler_CRawGameControllerVtbl_QueryInterface,
   9.327 +    IEventHandler_CRawGameControllerVtbl_AddRef,
   9.328 +    IEventHandler_CRawGameControllerVtbl_Release,
   9.329 +    IEventHandler_CRawGameControllerVtbl_InvokeRemoved
   9.330 +};
   9.331 +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController controller_removed = {
   9.332 +    &controller_removed_vtbl
   9.333 +};
   9.334 +
   9.335 +static int
   9.336 +WGI_JoystickInit(void)
   9.337 +{
   9.338 +    if (FAILED(WIN_CoInitialize())) {
   9.339 +        return SDL_SetError("CoInitialize() failed");
   9.340 +    }
   9.341 +
   9.342 +    HRESULT hr;
   9.343 +    HMODULE hModule = LoadLibraryA("combase.dll");
   9.344 +    if (hModule != NULL) {
   9.345 +        typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
   9.346 +        typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
   9.347 +        typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
   9.348 +
   9.349 +        WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
   9.350 +        WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
   9.351 +        RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
   9.352 +        if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
   9.353 +            LPTSTR pNamespace;
   9.354 +            HSTRING hNamespaceString;
   9.355 +
   9.356 +            pNamespace = L"Windows.Gaming.Input.RawGameController";
   9.357 +            hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
   9.358 +            if (SUCCEEDED(hr)) {
   9.359 +                hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IRawGameControllerStatics, &wgi.statics);
   9.360 +                if (!SUCCEEDED(hr)) {
   9.361 +                    SDL_SetError("Couldn't find IRawGameControllerStatics: 0x%x", hr);
   9.362 +                }
   9.363 +                WindowsDeleteStringFunc(hNamespaceString);
   9.364 +            }
   9.365 +
   9.366 +            pNamespace = L"Windows.Gaming.Input.ArcadeStick";
   9.367 +            hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
   9.368 +            if (SUCCEEDED(hr)) {
   9.369 +                hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IArcadeStickStatics, &wgi.arcade_stick_statics);
   9.370 +                if (SUCCEEDED(hr)) {
   9.371 +                    __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_QueryInterface(wgi.arcade_stick_statics, &IID_IArcadeStickStatics2, &wgi.arcade_stick_statics2);
   9.372 +                } else {
   9.373 +                    SDL_SetError("Couldn't find IID_IArcadeStickStatics: 0x%x", hr);
   9.374 +                }
   9.375 +                WindowsDeleteStringFunc(hNamespaceString);
   9.376 +            }
   9.377 +
   9.378 +            pNamespace = L"Windows.Gaming.Input.FlightStick";
   9.379 +            hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
   9.380 +            if (SUCCEEDED(hr)) {
   9.381 +                hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IFlightStickStatics, &wgi.flight_stick_statics);
   9.382 +                if (!SUCCEEDED(hr)) {
   9.383 +                    SDL_SetError("Couldn't find IID_IFlightStickStatics: 0x%x", hr);
   9.384 +                }
   9.385 +                WindowsDeleteStringFunc(hNamespaceString);
   9.386 +            }
   9.387 +
   9.388 +            pNamespace = L"Windows.Gaming.Input.Gamepad";
   9.389 +            hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
   9.390 +            if (SUCCEEDED(hr)) {
   9.391 +                hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IGamepadStatics, &wgi.gamepad_statics);
   9.392 +                if (SUCCEEDED(hr)) {
   9.393 +                    __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_QueryInterface(wgi.gamepad_statics, &IID_IGamepadStatics2, &wgi.gamepad_statics2);
   9.394 +                } else {
   9.395 +                    SDL_SetError("Couldn't find IGamepadStatics: 0x%x", hr);
   9.396 +                }
   9.397 +                WindowsDeleteStringFunc(hNamespaceString);
   9.398 +            }
   9.399 +
   9.400 +            pNamespace = L"Windows.Gaming.Input.RacingWheel";
   9.401 +            hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
   9.402 +            if (SUCCEEDED(hr)) {
   9.403 +                hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IRacingWheelStatics, &wgi.racing_wheel_statics);
   9.404 +                if (SUCCEEDED(hr)) {
   9.405 +                    __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_QueryInterface(wgi.racing_wheel_statics, &IID_IRacingWheelStatics2, &wgi.racing_wheel_statics2);
   9.406 +                } else {
   9.407 +                    SDL_SetError("Couldn't find IRacingWheelStatics: 0x%x", hr);
   9.408 +                }
   9.409 +                WindowsDeleteStringFunc(hNamespaceString);
   9.410 +            }
   9.411 +        }
   9.412 +        FreeLibrary(hModule);
   9.413 +    }
   9.414 +
   9.415 +    if (wgi.statics) {
   9.416 +        hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.statics, &controller_added, &wgi.controller_added_token);
   9.417 +        if (!SUCCEEDED(hr)) {
   9.418 +            SDL_SetError("add_RawGameControllerAdded() failed: 0x%x\n", hr);
   9.419 +        }
   9.420 +
   9.421 +        hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.statics, &controller_removed, &wgi.controller_removed_token);
   9.422 +        if (!SUCCEEDED(hr)) {
   9.423 +            SDL_SetError("add_RawGameControllerRemoved() failed: 0x%x\n", hr);
   9.424 +        }
   9.425 +    }
   9.426 +
   9.427 +    return 0;
   9.428 +}
   9.429 +
   9.430 +static int
   9.431 +WGI_JoystickGetCount(void)
   9.432 +{
   9.433 +    return wgi.controller_count;
   9.434 +}
   9.435 +
   9.436 +static void
   9.437 +WGI_JoystickDetect(void)
   9.438 +{
   9.439 +}
   9.440 +
   9.441 +static const char *
   9.442 +WGI_JoystickGetDeviceName(int device_index)
   9.443 +{
   9.444 +    return wgi.controllers[device_index].name;
   9.445 +}
   9.446 +
   9.447 +static int
   9.448 +WGI_JoystickGetDevicePlayerIndex(int device_index)
   9.449 +{
   9.450 +    return -1;
   9.451 +}
   9.452 +
   9.453 +static void
   9.454 +WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index)
   9.455 +{
   9.456 +}
   9.457 +
   9.458 +static SDL_JoystickGUID
   9.459 +WGI_JoystickGetDeviceGUID(int device_index)
   9.460 +{
   9.461 +    return wgi.controllers[device_index].guid;
   9.462 +}
   9.463 +
   9.464 +static SDL_JoystickID
   9.465 +WGI_JoystickGetDeviceInstanceID(int device_index)
   9.466 +{
   9.467 +    return wgi.controllers[device_index].instance_id;
   9.468 +}
   9.469 +
   9.470 +static int
   9.471 +WGI_JoystickOpen(SDL_Joystick * joystick, int device_index)
   9.472 +{
   9.473 +    WindowsGamingInputControllerState *state = &wgi.controllers[device_index];
   9.474 +    struct joystick_hwdata *hwdata;
   9.475 +    boolean wireless = SDL_FALSE;
   9.476 +
   9.477 +    hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
   9.478 +    if (!hwdata) {
   9.479 +        return SDL_OutOfMemory();
   9.480 +    }
   9.481 +    joystick->hwdata = hwdata;
   9.482 +
   9.483 +    hwdata->controller = state->controller;
   9.484 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(hwdata->controller);
   9.485 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID_IGameController, (void **)&hwdata->gamecontroller);
   9.486 +    __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID_IGameControllerBatteryInfo, (void **)&hwdata->battery);
   9.487 +
   9.488 +    if (wgi.gamepad_statics2) {
   9.489 +        __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->gamecontroller, &hwdata->gamepad);
   9.490 +    }
   9.491 +
   9.492 +    if (hwdata->gamecontroller) {
   9.493 +        __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->gamecontroller, &wireless);
   9.494 +    }
   9.495 +
   9.496 +    /* Initialize the joystick capabilities */
   9.497 +    joystick->nbuttons = state->nbuttons;
   9.498 +    joystick->naxes = state->naxes;
   9.499 +    joystick->nhats = state->nhats;
   9.500 +    joystick->epowerlevel = wireless ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
   9.501 +
   9.502 +    if (wireless && hwdata->battery) {
   9.503 +        HRESULT hr;
   9.504 +        __x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report;
   9.505 +
   9.506 +        hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report);
   9.507 +        if (SUCCEEDED(hr)) {
   9.508 +            int full_capacity = 0, curr_capacity = 0;
   9.509 +            __FIReference_1_int *full_capacityP, *curr_capacityP;
   9.510 +
   9.511 +            hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_FullChargeCapacityInMilliwattHours(report, &full_capacityP);
   9.512 +            if (SUCCEEDED(hr)) {
   9.513 +                __FIReference_1_int_get_Value(full_capacityP, &full_capacity);
   9.514 +                __FIReference_1_int_Release(full_capacityP);
   9.515 +            }
   9.516 +
   9.517 +            hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_RemainingCapacityInMilliwattHours(report, &curr_capacityP);
   9.518 +            if (SUCCEEDED(hr)) {
   9.519 +                __FIReference_1_int_get_Value(curr_capacityP, &curr_capacity);
   9.520 +                __FIReference_1_int_Release(curr_capacityP);
   9.521 +            }
   9.522 +
   9.523 +            if (full_capacity > 0) {
   9.524 +                float ratio = (float)curr_capacity / full_capacity;
   9.525 +
   9.526 +                if (ratio <= 0.05f) {
   9.527 +                    joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
   9.528 +                } else if (ratio <= 0.20f) {
   9.529 +                    joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
   9.530 +                } else if (ratio <= 0.70f) {
   9.531 +                    joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
   9.532 +                } else {
   9.533 +                    joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
   9.534 +                }
   9.535 +            }
   9.536 +            __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_Release(report);
   9.537 +        }
   9.538 +    }
   9.539 +    return 0;
   9.540 +}
   9.541 +
   9.542 +static int
   9.543 +WGI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
   9.544 +{
   9.545 +    struct joystick_hwdata *hwdata = joystick->hwdata;
   9.546 +
   9.547 +    if (hwdata->gamepad) {
   9.548 +        HRESULT hr;
   9.549 +        struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
   9.550 +
   9.551 +        SDL_zero(vibration);
   9.552 +        vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
   9.553 +        vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
   9.554 +        hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, vibration);
   9.555 +        if (SUCCEEDED(hr)) {
   9.556 +            return 0;
   9.557 +        } else {
   9.558 +            return SDL_SetError("Setting vibration failed: 0x%x\n", hr);
   9.559 +        }
   9.560 +    } else {
   9.561 +        return SDL_Unsupported();
   9.562 +    }
   9.563 +}
   9.564 +
   9.565 +static Uint8
   9.566 +ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value)
   9.567 +{
   9.568 +    switch (value) {
   9.569 +    case GameControllerSwitchPosition_Up:
   9.570 +        return SDL_HAT_UP;
   9.571 +    case GameControllerSwitchPosition_UpRight:
   9.572 +        return SDL_HAT_RIGHTUP;
   9.573 +    case GameControllerSwitchPosition_Right:
   9.574 +        return SDL_HAT_RIGHT;
   9.575 +    case GameControllerSwitchPosition_DownRight:
   9.576 +        return SDL_HAT_RIGHTDOWN;
   9.577 +    case GameControllerSwitchPosition_Down:
   9.578 +        return SDL_HAT_DOWN;
   9.579 +    case GameControllerSwitchPosition_DownLeft:
   9.580 +        return SDL_HAT_LEFTDOWN;
   9.581 +    case GameControllerSwitchPosition_Left:
   9.582 +        return SDL_HAT_LEFT;
   9.583 +    case GameControllerSwitchPosition_UpLeft:
   9.584 +        return SDL_HAT_LEFTUP;
   9.585 +    default:
   9.586 +        return SDL_HAT_CENTERED;
   9.587 +    }
   9.588 +}
   9.589 +
   9.590 +static void
   9.591 +WGI_JoystickUpdate(SDL_Joystick * joystick)
   9.592 +{
   9.593 +    struct joystick_hwdata *hwdata = joystick->hwdata;
   9.594 +    HRESULT hr;
   9.595 +    UINT32 nbuttons = joystick->nbuttons;
   9.596 +    boolean *buttons = SDL_stack_alloc(boolean, nbuttons);
   9.597 +    UINT32 nhats = joystick->nhats;
   9.598 +    __x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats);
   9.599 +    UINT32 naxes = joystick->naxes;
   9.600 +    DOUBLE *axes = SDL_stack_alloc(DOUBLE, naxes);
   9.601 +    UINT64 timestamp;
   9.602 +
   9.603 +    hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, &timestamp);
   9.604 +    if (SUCCEEDED(hr) && timestamp != hwdata->timestamp) {
   9.605 +        UINT32 i;
   9.606 +
   9.607 +        for (i = 0; i < nbuttons; ++i) {
   9.608 +            SDL_PrivateJoystickButton(joystick, i, buttons[i]);
   9.609 +        }
   9.610 +        for (i = 0; i < nhats; ++i) {
   9.611 +            SDL_PrivateJoystickHat(joystick, i, ConvertHatValue(hats[i]));
   9.612 +        }
   9.613 +        for (i = 0; i < naxes; ++i) {
   9.614 +            SDL_PrivateJoystickAxis(joystick, i, (int)(axes[i] * 65535) - 32768);
   9.615 +        }
   9.616 +        hwdata->timestamp = timestamp;
   9.617 +    }
   9.618 +
   9.619 +    SDL_stack_free(buttons);
   9.620 +    SDL_stack_free(hats);
   9.621 +    SDL_stack_free(axes);
   9.622 +}
   9.623 +
   9.624 +static void
   9.625 +WGI_JoystickClose(SDL_Joystick * joystick)
   9.626 +{
   9.627 +    struct joystick_hwdata *hwdata = joystick->hwdata;
   9.628 +
   9.629 +    if (hwdata) {
   9.630 +        if (hwdata->controller) {
   9.631 +            __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(hwdata->controller);
   9.632 +        }
   9.633 +        if (hwdata->gamecontroller) {
   9.634 +            __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->gamecontroller);
   9.635 +        }
   9.636 +        if (hwdata->battery) {
   9.637 +            __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_Release(hwdata->battery);
   9.638 +        }
   9.639 +        if (hwdata->gamepad) {
   9.640 +            __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(hwdata->gamepad);
   9.641 +        }
   9.642 +        SDL_free(hwdata);
   9.643 +    }
   9.644 +    joystick->hwdata = NULL;
   9.645 +}
   9.646 +
   9.647 +static void
   9.648 +WGI_JoystickQuit(void)
   9.649 +{
   9.650 +    if (wgi.statics) {
   9.651 +        while (wgi.controller_count > 0) {
   9.652 +            IEventHandler_CRawGameControllerVtbl_InvokeRemoved(&controller_removed, NULL, (__x_ABI_CWindows_CGaming_CInput_CIRawGameController **)wgi.controllers[wgi.controller_count - 1].controller);
   9.653 +        }
   9.654 +        if (wgi.controllers) {
   9.655 +            SDL_free(wgi.controllers);
   9.656 +        }
   9.657 +
   9.658 +        if (wgi.arcade_stick_statics) {
   9.659 +            __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_Release(wgi.arcade_stick_statics);
   9.660 +        }
   9.661 +        if (wgi.arcade_stick_statics2) {
   9.662 +            __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_Release(wgi.arcade_stick_statics2);
   9.663 +        }
   9.664 +        if (wgi.flight_stick_statics) {
   9.665 +            __x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_Release(wgi.flight_stick_statics);
   9.666 +        }
   9.667 +        if (wgi.gamepad_statics) {
   9.668 +            __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi.gamepad_statics);
   9.669 +        }
   9.670 +        if (wgi.gamepad_statics2) {
   9.671 +            __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_Release(wgi.gamepad_statics2);
   9.672 +        }
   9.673 +        if (wgi.racing_wheel_statics) {
   9.674 +            __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_Release(wgi.racing_wheel_statics);
   9.675 +        }
   9.676 +        if (wgi.racing_wheel_statics2) {
   9.677 +            __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_Release(wgi.racing_wheel_statics2);
   9.678 +        }
   9.679 +
   9.680 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerAdded(wgi.statics, wgi.controller_added_token);
   9.681 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.statics, wgi.controller_removed_token);
   9.682 +        __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.statics);
   9.683 +    }
   9.684 +    SDL_zero(wgi);
   9.685 +
   9.686 +    WIN_CoUninitialize();
   9.687 +}
   9.688 +
   9.689 +SDL_JoystickDriver SDL_WGI_JoystickDriver =
   9.690 +{
   9.691 +    WGI_JoystickInit,
   9.692 +    WGI_JoystickGetCount,
   9.693 +    WGI_JoystickDetect,
   9.694 +    WGI_JoystickGetDeviceName,
   9.695 +    WGI_JoystickGetDevicePlayerIndex,
   9.696 +    WGI_JoystickSetDevicePlayerIndex,
   9.697 +    WGI_JoystickGetDeviceGUID,
   9.698 +    WGI_JoystickGetDeviceInstanceID,
   9.699 +    WGI_JoystickOpen,
   9.700 +    WGI_JoystickRumble,
   9.701 +    WGI_JoystickUpdate,
   9.702 +    WGI_JoystickClose,
   9.703 +    WGI_JoystickQuit,
   9.704 +};
   9.705 +
   9.706 +#endif /* SDL_JOYSTICK_WGI */
   9.707 +
   9.708 +/* vi: set ts=4 sw=4 expandtab: */
    10.1 --- a/src/sensor/windows/SDL_windowssensor.c	Fri Apr 17 21:30:58 2020 -0700
    10.2 +++ b/src/sensor/windows/SDL_windowssensor.c	Sat Apr 18 21:41:37 2020 -0700
    10.3 @@ -41,10 +41,6 @@
    10.4  DEFINE_GUID(IID_SensorManagerEvents, 0x9B3B0B86, 0x266A, 0x4AAD, 0xB2, 0x1F, 0xFD, 0xE5, 0x50, 0x10, 0x01, 0xB7);
    10.5  DEFINE_GUID(IID_SensorEvents, 0x5D8DCC91, 0x4641, 0x47E7, 0xB7, 0xC3, 0xB7, 0x4F, 0x48, 0xA6, 0xC3, 0x91);
    10.6  
    10.7 -DEFINE_PROPERTYKEY(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 10); //[VT_R8]
    10.8 -DEFINE_PROPERTYKEY(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 11); //[VT_R8]
    10.9 -DEFINE_PROPERTYKEY(SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, 0X3F8A69A2, 0X7C5, 0X4E48, 0XA9, 0X65, 0XCD, 0X79, 0X7A, 0XAB, 0X56, 0XD5, 12); //[VT_R8]
   10.10 -
   10.11  typedef struct
   10.12  {
   10.13      SDL_SensorID id;
    11.1 --- a/test/controllermap.c	Fri Apr 17 21:30:58 2020 -0700
    11.2 +++ b/test/controllermap.c	Sat Apr 18 21:41:37 2020 -0700
    11.3 @@ -155,6 +155,9 @@
    11.4  static Uint32 s_unPendingAdvanceTime;
    11.5  static SDL_bool s_bBindingComplete;
    11.6  
    11.7 +static SDL_Window *window;
    11.8 +static SDL_bool done = SDL_FALSE;
    11.9 +
   11.10  SDL_Texture *
   11.11  LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
   11.12  {
   11.13 @@ -357,30 +360,18 @@
   11.14  static void
   11.15  WatchJoystick(SDL_Joystick * joystick)
   11.16  {
   11.17 -    SDL_Window *window = NULL;
   11.18      SDL_Renderer *screen = NULL;
   11.19      SDL_Texture *background, *button, *axis, *marker;
   11.20      const char *name = NULL;
   11.21 -    SDL_bool done = SDL_FALSE;
   11.22      SDL_Event event;
   11.23      SDL_Rect dst;
   11.24      Uint8 alpha=200, alpha_step = -1;
   11.25      Uint32 alpha_ticks = 0;
   11.26      SDL_JoystickID nJoystickID;
   11.27  
   11.28 -    /* Create a window to display joystick axis position */
   11.29 -    window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
   11.30 -                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
   11.31 -                              SCREEN_HEIGHT, 0);
   11.32 -    if (window == NULL) {
   11.33 -        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
   11.34 -        return;
   11.35 -    }
   11.36 -
   11.37      screen = SDL_CreateRenderer(window, -1, 0);
   11.38      if (screen == NULL) {
   11.39          SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
   11.40 -        SDL_DestroyWindow(window);
   11.41          return;
   11.42      }
   11.43      
   11.44 @@ -705,7 +696,6 @@
   11.45      s_arrAxisState = NULL;
   11.46      
   11.47      SDL_DestroyRenderer(screen);
   11.48 -    SDL_DestroyWindow(window);
   11.49  }
   11.50  
   11.51  int
   11.52 @@ -724,6 +714,34 @@
   11.53          exit(1);
   11.54      }
   11.55  
   11.56 +    /* Create a window to display joystick axis position */
   11.57 +    window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
   11.58 +                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
   11.59 +                              SCREEN_HEIGHT, 0);
   11.60 +    if (window == NULL) {
   11.61 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
   11.62 +        return 2;
   11.63 +    }
   11.64 +
   11.65 +    while (SDL_NumJoysticks() == 0) {
   11.66 +        SDL_Event event;
   11.67 +
   11.68 +        while (SDL_PollEvent(&event) > 0) {
   11.69 +            switch (event.type) {
   11.70 +            case SDL_KEYDOWN:
   11.71 +                if ((event.key.keysym.sym != SDLK_ESCAPE)) {
   11.72 +                    break;
   11.73 +                }
   11.74 +                /* Fall through to signal quit */
   11.75 +            case SDL_QUIT:
   11.76 +                done = SDL_TRUE;
   11.77 +                break;
   11.78 +            default:
   11.79 +                break;
   11.80 +            }
   11.81 +        }
   11.82 +    }
   11.83 +
   11.84      /* Print information about the joysticks */
   11.85      SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks());
   11.86      for (i = 0; i < SDL_NumJoysticks(); ++i) {
   11.87 @@ -748,28 +766,16 @@
   11.88          }
   11.89      }
   11.90  
   11.91 -#ifdef __ANDROID__
   11.92 -    if (SDL_NumJoysticks() > 0) {
   11.93 -#else
   11.94 -    if (argv[1]) {
   11.95 -#endif
   11.96 -        int device;
   11.97 -#ifdef __ANDROID__
   11.98 -        device = 0;
   11.99 -#else
  11.100 -        device = atoi(argv[1]);
  11.101 -#endif
  11.102 -        joystick = SDL_JoystickOpen(device);
  11.103 -        if (joystick == NULL) {
  11.104 -            SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
  11.105 -        } else {
  11.106 -            WatchJoystick(joystick);
  11.107 -            SDL_JoystickClose(joystick);
  11.108 -        }
  11.109 +    joystick = SDL_JoystickOpen(0);
  11.110 +    if (joystick == NULL) {
  11.111 +        SDL_Log("Couldn't open joystick 0: %s\n", SDL_GetError());
  11.112 +    } else {
  11.113 +        WatchJoystick(joystick);
  11.114 +        SDL_JoystickClose(joystick);
  11.115      }
  11.116 -    else {
  11.117 -        SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt");
  11.118 -    }
  11.119 +
  11.120 +    SDL_DestroyWindow(window);
  11.121 +
  11.122      SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
  11.123  
  11.124      return 0;
    12.1 --- a/test/testgamecontroller.c	Fri Apr 17 21:30:58 2020 -0700
    12.2 +++ b/test/testgamecontroller.c	Sat Apr 18 21:41:37 2020 -0700
    12.3 @@ -61,10 +61,12 @@
    12.4      {375, -20, 0.0},    /* TRIGGERRIGHT */
    12.5  };
    12.6  
    12.7 +SDL_Window *window = NULL;
    12.8  SDL_Renderer *screen = NULL;
    12.9  SDL_bool retval = SDL_FALSE;
   12.10  SDL_bool done = SDL_FALSE;
   12.11  SDL_Texture *background, *button, *axis;
   12.12 +SDL_GameController *gamecontroller;
   12.13  
   12.14  static SDL_Texture *
   12.15  LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
   12.16 @@ -94,12 +96,29 @@
   12.17      return texture;
   12.18  }
   12.19  
   12.20 +static void
   12.21 +UpdateWindowTitle()
   12.22 +{
   12.23 +    const char *name = SDL_GameControllerName(gamecontroller);
   12.24 +    const char *basetitle = "Game Controller Test: ";
   12.25 +    const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1;
   12.26 +    char *title = (char *)SDL_malloc(titlelen);
   12.27 +
   12.28 +    retval = SDL_FALSE;
   12.29 +    done = SDL_FALSE;
   12.30 +
   12.31 +    if (title) {
   12.32 +        SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
   12.33 +        SDL_SetWindowTitle(window, title);
   12.34 +        SDL_free(title);
   12.35 +    }
   12.36 +}
   12.37 +
   12.38  void
   12.39  loop(void *arg)
   12.40  {
   12.41      SDL_Event event;
   12.42      int i;
   12.43 -    SDL_GameController *gamecontroller = (SDL_GameController *)arg;
   12.44  
   12.45      /* blank screen, set up for drawing this frame. */
   12.46      SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
   12.47 @@ -108,6 +127,29 @@
   12.48  
   12.49      while (SDL_PollEvent(&event)) {
   12.50          switch (event.type) {
   12.51 +        case SDL_CONTROLLERDEVICEADDED:
   12.52 +            SDL_Log("Game controller device %d added.\n", (int) event.cdevice.which);
   12.53 +            if (!gamecontroller) {
   12.54 +                gamecontroller = SDL_GameControllerOpen(event.cdevice.which);
   12.55 +                if (gamecontroller) {
   12.56 +                    UpdateWindowTitle();
   12.57 +                } else {
   12.58 +                    SDL_Log("Couldn't open controller: %s\n", SDL_GetError());
   12.59 +                }
   12.60 +            }
   12.61 +            break;
   12.62 +
   12.63 +        case SDL_CONTROLLERDEVICEREMOVED:
   12.64 +            SDL_Log("Game controller device %d removed.\n", (int) event.cdevice.which);
   12.65 +            if (gamecontroller && event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) {
   12.66 +                SDL_GameControllerClose(gamecontroller);
   12.67 +                gamecontroller = SDL_GameControllerOpen(0);
   12.68 +                if (gamecontroller) {
   12.69 +                    UpdateWindowTitle();
   12.70 +                }
   12.71 +            }
   12.72 +            break;
   12.73 +
   12.74          case SDL_CONTROLLERAXISMOTION:
   12.75              SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
   12.76              break;
   12.77 @@ -128,42 +170,39 @@
   12.78          }
   12.79      }
   12.80  
   12.81 -    /* Update visual controller state */
   12.82 -    for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
   12.83 -        if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
   12.84 -            const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
   12.85 -            SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
   12.86 +    if (gamecontroller) {
   12.87 +        /* Update visual controller state */
   12.88 +        for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
   12.89 +            if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
   12.90 +                const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
   12.91 +                SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
   12.92 +            }
   12.93 +        }
   12.94 +
   12.95 +        for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
   12.96 +            const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
   12.97 +            const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
   12.98 +            if (value < -deadzone) {
   12.99 +                const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
  12.100 +                const double angle = axis_positions[i].angle;
  12.101 +                SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
  12.102 +            } else if (value > deadzone) {
  12.103 +                const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
  12.104 +                const double angle = axis_positions[i].angle + 180.0;
  12.105 +                SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
  12.106 +            }
  12.107 +        }
  12.108 +
  12.109 +        /* Update rumble based on trigger state */
  12.110 +        {
  12.111 +            Uint16 low_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) * 2;
  12.112 +            Uint16 high_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * 2;
  12.113 +            SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250);
  12.114          }
  12.115      }
  12.116  
  12.117 -    for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
  12.118 -        const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
  12.119 -        const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
  12.120 -        if (value < -deadzone) {
  12.121 -            const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
  12.122 -            const double angle = axis_positions[i].angle;
  12.123 -            SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
  12.124 -        } else if (value > deadzone) {
  12.125 -            const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
  12.126 -            const double angle = axis_positions[i].angle + 180.0;
  12.127 -            SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
  12.128 -        }
  12.129 -    }
  12.130 -
  12.131 -    /* Update rumble based on trigger state */
  12.132 -    {
  12.133 -        Uint16 low_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) * 2;
  12.134 -        Uint16 high_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * 2;
  12.135 -        SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250);
  12.136 -    }
  12.137 -
  12.138      SDL_RenderPresent(screen);
  12.139  
  12.140 -    if (!SDL_GameControllerGetAttached(gamecontroller)) {
  12.141 -        done = SDL_TRUE;
  12.142 -        retval = SDL_TRUE;  /* keep going, wait for reattach. */
  12.143 -    }
  12.144 -
  12.145  #ifdef __EMSCRIPTEN__
  12.146      if (done) {
  12.147          emscripten_cancel_main_loop();
  12.148 @@ -171,92 +210,12 @@
  12.149  #endif
  12.150  }
  12.151  
  12.152 -SDL_bool
  12.153 -WatchGameController(SDL_GameController * gamecontroller)
  12.154 -{
  12.155 -    const char *name = SDL_GameControllerName(gamecontroller);
  12.156 -    const char *basetitle = "Game Controller Test: ";
  12.157 -    const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1;
  12.158 -    char *title = (char *)SDL_malloc(titlelen);
  12.159 -    SDL_Window *window = NULL;
  12.160 -
  12.161 -    retval = SDL_FALSE;
  12.162 -    done = SDL_FALSE;
  12.163 -
  12.164 -    if (title) {
  12.165 -        SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
  12.166 -    }
  12.167 -
  12.168 -    /* Create a window to display controller state */
  12.169 -    window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED,
  12.170 -                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
  12.171 -                              SCREEN_HEIGHT, 0);
  12.172 -    SDL_free(title);
  12.173 -    title = NULL;
  12.174 -    if (window == NULL) {
  12.175 -        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
  12.176 -        return SDL_FALSE;
  12.177 -    }
  12.178 -
  12.179 -    screen = SDL_CreateRenderer(window, -1, 0);
  12.180 -    if (screen == NULL) {
  12.181 -        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
  12.182 -        SDL_DestroyWindow(window);
  12.183 -        return SDL_FALSE;
  12.184 -    }
  12.185 -
  12.186 -    SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
  12.187 -    SDL_RenderClear(screen);
  12.188 -    SDL_RenderPresent(screen);
  12.189 -    SDL_RaiseWindow(window);
  12.190 -
  12.191 -    /* scale for platforms that don't give you the window size you asked for. */
  12.192 -    SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
  12.193 -
  12.194 -    background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
  12.195 -    button = LoadTexture(screen, "button.bmp", SDL_TRUE);
  12.196 -    axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
  12.197 -
  12.198 -    if (!background || !button || !axis) {
  12.199 -        SDL_DestroyRenderer(screen);
  12.200 -        SDL_DestroyWindow(window);
  12.201 -        return SDL_FALSE;
  12.202 -    }
  12.203 -    SDL_SetTextureColorMod(button, 10, 255, 21);
  12.204 -    SDL_SetTextureColorMod(axis, 10, 255, 21);
  12.205 -
  12.206 -    /* !!! FIXME: */
  12.207 -    /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
  12.208 -
  12.209 -    /* Print info about the controller we are watching */
  12.210 -    SDL_Log("Watching controller %s\n",  name ? name : "Unknown Controller");
  12.211 -
  12.212 -    /* Loop, getting controller events! */
  12.213 -#ifdef __EMSCRIPTEN__
  12.214 -    emscripten_set_main_loop_arg(loop, gamecontroller, 0, 1);
  12.215 -#else
  12.216 -    while (!done) {
  12.217 -        loop(gamecontroller);
  12.218 -    }
  12.219 -#endif
  12.220 -
  12.221 -    SDL_DestroyRenderer(screen);
  12.222 -    screen = NULL;
  12.223 -    background = NULL;
  12.224 -    button = NULL;
  12.225 -    axis = NULL;
  12.226 -    SDL_DestroyWindow(window);
  12.227 -    return retval;
  12.228 -}
  12.229 -
  12.230  int
  12.231  main(int argc, char *argv[])
  12.232  {
  12.233      int i;
  12.234      int nController = 0;
  12.235 -    int retcode = 0;
  12.236      char guid[64];
  12.237 -    SDL_GameController *gamecontroller;
  12.238  
  12.239      /* Enable standard application logging */
  12.240      SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
  12.241 @@ -270,7 +229,7 @@
  12.242      SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
  12.243  
  12.244      /* Print information about the mappings */
  12.245 -    if (!argv[1]) {
  12.246 +    if (argv[1] && SDL_strcmp(argv[1], "--mappings") == 0) {
  12.247          SDL_Log("Supported mappings:\n");
  12.248          for (i = 0; i < SDL_GameControllerNumMappings(); ++i) {
  12.249              char *mapping = SDL_GameControllerMappingForIndex(i);
  12.250 @@ -290,8 +249,7 @@
  12.251          SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
  12.252                                    guid, sizeof (guid));
  12.253  
  12.254 -        if ( SDL_IsGameController(i) )
  12.255 -        {
  12.256 +        if ( SDL_IsGameController(i) ) {
  12.257              nController++;
  12.258              name = SDL_GameControllerNameForIndex(i);
  12.259              switch (SDL_GameControllerTypeForIndex(i)) {
  12.260 @@ -327,62 +285,62 @@
  12.261      }
  12.262      SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks());
  12.263  
  12.264 -    if (argv[1]) {
  12.265 -        SDL_bool reportederror = SDL_FALSE;
  12.266 -        SDL_bool keepGoing = SDL_TRUE;
  12.267 -        SDL_Event event;
  12.268 -        int device = atoi(argv[1]);
  12.269 -        if (device >= SDL_NumJoysticks()) {
  12.270 -            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%i is an invalid joystick index.\n", device);
  12.271 -            retcode = 1;
  12.272 -        } else {
  12.273 -            SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(device),
  12.274 -                                      guid, sizeof (guid));
  12.275 -            SDL_Log("Attempting to open device %i, guid %s\n", device, guid);
  12.276 -            gamecontroller = SDL_GameControllerOpen(device);
  12.277 -
  12.278 -            if (gamecontroller != NULL) {
  12.279 -                SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
  12.280 -            }
  12.281 +    /* Create a window to display controller state */
  12.282 +    window = SDL_CreateWindow("Game Controller Test", SDL_WINDOWPOS_CENTERED,
  12.283 +                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
  12.284 +                              SCREEN_HEIGHT, 0);
  12.285 +    if (window == NULL) {
  12.286 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
  12.287 +        return 2;
  12.288 +    }
  12.289  
  12.290 -            while (keepGoing) {
  12.291 -                if (gamecontroller == NULL) {
  12.292 -                    if (!reportederror) {
  12.293 -                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open gamecontroller %d: %s\n", device, SDL_GetError());
  12.294 -                        retcode = 1;
  12.295 -                        keepGoing = SDL_FALSE;
  12.296 -                        reportederror = SDL_TRUE;
  12.297 -                    }
  12.298 -                } else {
  12.299 -                    reportederror = SDL_FALSE;
  12.300 -                    keepGoing = WatchGameController(gamecontroller);
  12.301 -                    SDL_GameControllerClose(gamecontroller);
  12.302 -                }
  12.303 -
  12.304 -                gamecontroller = NULL;
  12.305 -                if (keepGoing) {
  12.306 -                    SDL_Log("Waiting for attach\n");
  12.307 -                }
  12.308 -                while (keepGoing) {
  12.309 -                    SDL_WaitEvent(&event);
  12.310 -                    if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN)
  12.311 -                        || (event.type == SDL_MOUSEBUTTONDOWN)) {
  12.312 -                        keepGoing = SDL_FALSE;
  12.313 -                    } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
  12.314 -                        gamecontroller = SDL_GameControllerOpen(event.cdevice.which);
  12.315 -                        if (gamecontroller != NULL) {
  12.316 -                            SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
  12.317 -                        }
  12.318 -                        break;
  12.319 -                    }
  12.320 -                }
  12.321 -            }
  12.322 -        }
  12.323 +    screen = SDL_CreateRenderer(window, -1, 0);
  12.324 +    if (screen == NULL) {
  12.325 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
  12.326 +        SDL_DestroyWindow(window);
  12.327 +        return 2;
  12.328      }
  12.329  
  12.330 +    SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
  12.331 +    SDL_RenderClear(screen);
  12.332 +    SDL_RenderPresent(screen);
  12.333 +
  12.334 +    /* scale for platforms that don't give you the window size you asked for. */
  12.335 +    SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
  12.336 +
  12.337 +    background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
  12.338 +    button = LoadTexture(screen, "button.bmp", SDL_TRUE);
  12.339 +    axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
  12.340 +
  12.341 +    if (!background || !button || !axis) {
  12.342 +        SDL_DestroyRenderer(screen);
  12.343 +        SDL_DestroyWindow(window);
  12.344 +        return 2;
  12.345 +    }
  12.346 +    SDL_SetTextureColorMod(button, 10, 255, 21);
  12.347 +    SDL_SetTextureColorMod(axis, 10, 255, 21);
  12.348 +
  12.349 +    /* !!! FIXME: */
  12.350 +    /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
  12.351 +
  12.352 +    /* Loop, getting controller events! */
  12.353 +#ifdef __EMSCRIPTEN__
  12.354 +    emscripten_set_main_loop_arg(loop, NULL, 0, 1);
  12.355 +#else
  12.356 +    while (!done) {
  12.357 +        loop(NULL);
  12.358 +    }
  12.359 +#endif
  12.360 +
  12.361 +    SDL_DestroyRenderer(screen);
  12.362 +    screen = NULL;
  12.363 +    background = NULL;
  12.364 +    button = NULL;
  12.365 +    axis = NULL;
  12.366 +    SDL_DestroyWindow(window);
  12.367      SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
  12.368  
  12.369 -    return retcode;
  12.370 +    return 0;
  12.371  }
  12.372  
  12.373  #else
    13.1 --- a/test/testjoystick.c	Fri Apr 17 21:30:58 2020 -0700
    13.2 +++ b/test/testjoystick.c	Sat Apr 18 21:41:37 2020 -0700
    13.3 @@ -32,9 +32,62 @@
    13.4  #define SCREEN_HEIGHT   480
    13.5  #endif
    13.6  
    13.7 -SDL_Renderer *screen = NULL;
    13.8 -SDL_bool retval = SDL_FALSE;
    13.9 -SDL_bool done = SDL_FALSE;
   13.10 +static SDL_Window *window = NULL;
   13.11 +static SDL_Renderer *screen = NULL;
   13.12 +static SDL_Joystick *joystick = NULL;
   13.13 +static SDL_bool done = SDL_FALSE;
   13.14 +
   13.15 +static void
   13.16 +PrintJoystick(SDL_Joystick *joystick)
   13.17 +{
   13.18 +    const char *type;
   13.19 +    char guid[64];
   13.20 +
   13.21 +    SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick);
   13.22 +    SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guid, sizeof (guid));
   13.23 +    switch (SDL_JoystickGetType(joystick)) {
   13.24 +    case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
   13.25 +        type = "Game Controller";
   13.26 +        break;
   13.27 +    case SDL_JOYSTICK_TYPE_WHEEL:
   13.28 +        type = "Wheel";
   13.29 +        break;
   13.30 +    case SDL_JOYSTICK_TYPE_ARCADE_STICK:
   13.31 +        type = "Arcade Stick";
   13.32 +        break;
   13.33 +    case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
   13.34 +        type = "Flight Stick";
   13.35 +        break;
   13.36 +    case SDL_JOYSTICK_TYPE_DANCE_PAD:
   13.37 +        type = "Dance Pad";
   13.38 +        break;
   13.39 +    case SDL_JOYSTICK_TYPE_GUITAR:
   13.40 +        type = "Guitar";
   13.41 +        break;
   13.42 +    case SDL_JOYSTICK_TYPE_DRUM_KIT:
   13.43 +        type = "Drum Kit";
   13.44 +        break;
   13.45 +    case SDL_JOYSTICK_TYPE_ARCADE_PAD:
   13.46 +        type = "Arcade Pad";
   13.47 +        break;
   13.48 +    case SDL_JOYSTICK_TYPE_THROTTLE:
   13.49 +        type = "Throttle";
   13.50 +        break;
   13.51 +    default:
   13.52 +        type = "Unknown";
   13.53 +        break;
   13.54 +    }
   13.55 +    SDL_Log("Joystick\n");
   13.56 +    SDL_Log("       name: %s\n", SDL_JoystickName(joystick));
   13.57 +    SDL_Log("       type: %s\n", type);
   13.58 +    SDL_Log("       axes: %d\n", SDL_JoystickNumAxes(joystick));
   13.59 +    SDL_Log("      balls: %d\n", SDL_JoystickNumBalls(joystick));
   13.60 +    SDL_Log("       hats: %d\n", SDL_JoystickNumHats(joystick));
   13.61 +    SDL_Log("    buttons: %d\n", SDL_JoystickNumButtons(joystick));
   13.62 +    SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
   13.63 +    SDL_Log("       guid: %s\n", guid);
   13.64 +    SDL_Log("    VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
   13.65 +}
   13.66  
   13.67  static void
   13.68  DrawRect(SDL_Renderer *r, const int x, const int y, const int w, const int h)
   13.69 @@ -48,72 +101,89 @@
   13.70  {
   13.71      SDL_Event event;
   13.72      int i;
   13.73 -    SDL_Joystick *joystick = (SDL_Joystick *)arg;
   13.74  
   13.75 -        /* blank screen, set up for drawing this frame. */
   13.76 +    /* blank screen, set up for drawing this frame. */
   13.77      SDL_SetRenderDrawColor(screen, 0x0, 0x0, 0x0, SDL_ALPHA_OPAQUE);
   13.78 -        SDL_RenderClear(screen);
   13.79 +    SDL_RenderClear(screen);
   13.80 +
   13.81 +    while (SDL_PollEvent(&event)) {
   13.82 +        switch (event.type) {
   13.83  
   13.84 -        while (SDL_PollEvent(&event)) {
   13.85 -            switch (event.type) {
   13.86 +        case SDL_JOYDEVICEADDED:
   13.87 +            SDL_Log("Joystick device %d added.\n", (int) event.jdevice.which);
   13.88 +            if (!joystick) {
   13.89 +                joystick = SDL_JoystickOpen(event.jdevice.which);
   13.90 +                if (joystick) {
   13.91 +                    PrintJoystick(joystick);
   13.92 +                } else {
   13.93 +                    SDL_Log("Couldn't open joystick: %s\n", SDL_GetError());
   13.94 +                }
   13.95 +            }
   13.96 +            break;
   13.97  
   13.98 -            case SDL_JOYDEVICEREMOVED:
   13.99 -                SDL_Log("Joystick device %d removed.\n", (int) event.jdevice.which);
  13.100 -                SDL_Log("Our instance ID is %d\n", (int) SDL_JoystickInstanceID(joystick));
  13.101 -                break;
  13.102 +        case SDL_JOYDEVICEREMOVED:
  13.103 +            SDL_Log("Joystick device %d removed.\n", (int) event.jdevice.which);
  13.104 +            if (event.jdevice.which == SDL_JoystickInstanceID(joystick)) {
  13.105 +                SDL_JoystickClose(joystick);
  13.106 +                joystick = SDL_JoystickOpen(0);
  13.107 +            }
  13.108 +            break;
  13.109  
  13.110 -            case SDL_JOYAXISMOTION:
  13.111 -                SDL_Log("Joystick %d axis %d value: %d\n",
  13.112 -                       event.jaxis.which,
  13.113 -                       event.jaxis.axis, event.jaxis.value);
  13.114 -                break;
  13.115 -            case SDL_JOYHATMOTION:
  13.116 -                SDL_Log("Joystick %d hat %d value:",
  13.117 -                       event.jhat.which, event.jhat.hat);
  13.118 -                if (event.jhat.value == SDL_HAT_CENTERED)
  13.119 -                    SDL_Log(" centered");
  13.120 -                if (event.jhat.value & SDL_HAT_UP)
  13.121 -                    SDL_Log(" up");
  13.122 -                if (event.jhat.value & SDL_HAT_RIGHT)
  13.123 -                    SDL_Log(" right");
  13.124 -                if (event.jhat.value & SDL_HAT_DOWN)
  13.125 -                    SDL_Log(" down");
  13.126 -                if (event.jhat.value & SDL_HAT_LEFT)
  13.127 -                    SDL_Log(" left");
  13.128 -                SDL_Log("\n");
  13.129 -                break;
  13.130 -            case SDL_JOYBALLMOTION:
  13.131 -                SDL_Log("Joystick %d ball %d delta: (%d,%d)\n",
  13.132 -                       event.jball.which,
  13.133 -                       event.jball.ball, event.jball.xrel, event.jball.yrel);
  13.134 -                break;
  13.135 -            case SDL_JOYBUTTONDOWN:
  13.136 -                SDL_Log("Joystick %d button %d down\n",
  13.137 -                       event.jbutton.which, event.jbutton.button);
  13.138 -                /* First button triggers a 0.5 second full strength rumble */
  13.139 -                if (event.jbutton.button == 0) {
  13.140 -                    SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500);
  13.141 -                }
  13.142 -                break;
  13.143 -            case SDL_JOYBUTTONUP:
  13.144 -                SDL_Log("Joystick %d button %d up\n",
  13.145 -                       event.jbutton.which, event.jbutton.button);
  13.146 -                break;
  13.147 -            case SDL_KEYDOWN:
  13.148 -                if ((event.key.keysym.sym != SDLK_ESCAPE) &&
  13.149 -                    (event.key.keysym.sym != SDLK_AC_BACK)) {
  13.150 -                    break;
  13.151 -                }
  13.152 -                /* Fall through to signal quit */
  13.153 -            case SDL_FINGERDOWN:
  13.154 -            case SDL_MOUSEBUTTONDOWN:
  13.155 -            case SDL_QUIT:
  13.156 -                done = SDL_TRUE;
  13.157 -                break;
  13.158 -            default:
  13.159 +        case SDL_JOYAXISMOTION:
  13.160 +            SDL_Log("Joystick %d axis %d value: %d\n",
  13.161 +                   event.jaxis.which,
  13.162 +                   event.jaxis.axis, event.jaxis.value);
  13.163 +            break;
  13.164 +        case SDL_JOYHATMOTION:
  13.165 +            SDL_Log("Joystick %d hat %d value:",
  13.166 +                   event.jhat.which, event.jhat.hat);
  13.167 +            if (event.jhat.value == SDL_HAT_CENTERED)
  13.168 +                SDL_Log(" centered");
  13.169 +            if (event.jhat.value & SDL_HAT_UP)
  13.170 +                SDL_Log(" up");
  13.171 +            if (event.jhat.value & SDL_HAT_RIGHT)
  13.172 +                SDL_Log(" right");
  13.173 +            if (event.jhat.value & SDL_HAT_DOWN)
  13.174 +                SDL_Log(" down");
  13.175 +            if (event.jhat.value & SDL_HAT_LEFT)
  13.176 +                SDL_Log(" left");
  13.177 +            SDL_Log("\n");
  13.178 +            break;
  13.179 +        case SDL_JOYBALLMOTION:
  13.180 +            SDL_Log("Joystick %d ball %d delta: (%d,%d)\n",
  13.181 +                   event.jball.which,
  13.182 +                   event.jball.ball, event.jball.xrel, event.jball.yrel);
  13.183 +            break;
  13.184 +        case SDL_JOYBUTTONDOWN:
  13.185 +            SDL_Log("Joystick %d button %d down\n",
  13.186 +                   event.jbutton.which, event.jbutton.button);
  13.187 +            /* First button triggers a 0.5 second full strength rumble */
  13.188 +            if (event.jbutton.button == 0) {
  13.189 +                SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500);
  13.190 +            }
  13.191 +            break;
  13.192 +        case SDL_JOYBUTTONUP:
  13.193 +            SDL_Log("Joystick %d button %d up\n",
  13.194 +                   event.jbutton.which, event.jbutton.button);
  13.195 +            break;
  13.196 +        case SDL_KEYDOWN:
  13.197 +            if ((event.key.keysym.sym != SDLK_ESCAPE) &&
  13.198 +                (event.key.keysym.sym != SDLK_AC_BACK)) {
  13.199                  break;
  13.200              }
  13.201 +            /* Fall through to signal quit */
  13.202 +        case SDL_FINGERDOWN:
  13.203 +        case SDL_MOUSEBUTTONDOWN:
  13.204 +        case SDL_QUIT:
  13.205 +            done = SDL_TRUE;
  13.206 +            break;
  13.207 +        default:
  13.208 +            break;
  13.209          }
  13.210 +    }
  13.211 +
  13.212 +    if (joystick) {
  13.213 +
  13.214          /* Update visual joystick state */
  13.215          SDL_SetRenderDrawColor(screen, 0x00, 0xFF, 0x00, SDL_ALPHA_OPAQUE);
  13.216          for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i) {
  13.217 @@ -172,13 +242,9 @@
  13.218  
  13.219              DrawRect(screen, x, y, 8, 8);
  13.220          }
  13.221 -
  13.222 -        SDL_RenderPresent(screen);
  13.223 +    }
  13.224  
  13.225 -        if (SDL_JoystickGetAttached( joystick ) == 0) {
  13.226 -            done = SDL_TRUE;
  13.227 -            retval = SDL_TRUE;  /* keep going, wait for reattach. */
  13.228 -        }
  13.229 +    SDL_RenderPresent(screen);
  13.230  
  13.231  #ifdef __EMSCRIPTEN__
  13.232      if (done) {
  13.233 @@ -187,14 +253,19 @@
  13.234  #endif
  13.235  }
  13.236  
  13.237 -static SDL_bool
  13.238 -WatchJoystick(SDL_Joystick * joystick)
  13.239 +int
  13.240 +main(int argc, char *argv[])
  13.241  {
  13.242 -    SDL_Window *window = NULL;
  13.243 -    const char *name = NULL;
  13.244 +    SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
  13.245 +
  13.246 +    /* Enable standard application logging */
  13.247 +    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
  13.248  
  13.249 -    retval = SDL_FALSE;
  13.250 -    done = SDL_FALSE;
  13.251 +    /* Initialize SDL (Note: video is required to start event loop) */
  13.252 +    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
  13.253 +        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
  13.254 +        exit(1);
  13.255 +    }
  13.256  
  13.257      /* Create a window to display joystick axis position */
  13.258      window = SDL_CreateWindow("Joystick Test", SDL_WINDOWPOS_CENTERED,
  13.259 @@ -215,159 +286,19 @@
  13.260      SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
  13.261      SDL_RenderClear(screen);
  13.262      SDL_RenderPresent(screen);
  13.263 -    SDL_RaiseWindow(window);
  13.264 -
  13.265 -    /* Print info about the joystick we are watching */
  13.266 -    name = SDL_JoystickName(joystick);
  13.267 -    SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
  13.268 -           name ? name : "Unknown Joystick");
  13.269 -    SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
  13.270 -           SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
  13.271 -           SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
  13.272  
  13.273      /* Loop, getting joystick events! */
  13.274  #ifdef __EMSCRIPTEN__
  13.275 -    emscripten_set_main_loop_arg(loop, joystick, 0, 1);
  13.276 +    emscripten_set_main_loop_arg(loop, NULL, 0, 1);
  13.277  #else
  13.278      while (!done) {
  13.279 -        loop(joystick);
  13.280 +        loop(NULL);
  13.281      }
  13.282  #endif
  13.283  
  13.284      SDL_DestroyRenderer(screen);
  13.285 -    screen = NULL;
  13.286      SDL_DestroyWindow(window);
  13.287 -    return retval;
  13.288 -}
  13.289 -
  13.290 -int
  13.291 -main(int argc, char *argv[])
  13.292 -{
  13.293 -    const char *name, *type;
  13.294 -    int i;
  13.295 -    SDL_Joystick *joystick;
  13.296 -
  13.297 -    SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
  13.298 -
  13.299 -    /* Enable standard application logging */
  13.300 -    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
  13.301 -
  13.302 -    /* Initialize SDL (Note: video is required to start event loop) */
  13.303 -    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
  13.304 -        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
  13.305 -        exit(1);
  13.306 -    }
  13.307  
  13.308 -    /* Print information about the joysticks */
  13.309 -    SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks());
  13.310 -    for (i = 0; i < SDL_NumJoysticks(); ++i) {
  13.311 -        name = SDL_JoystickNameForIndex(i);
  13.312 -        SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick");
  13.313 -        joystick = SDL_JoystickOpen(i);
  13.314 -        if (joystick == NULL) {
  13.315 -            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i,
  13.316 -                    SDL_GetError());
  13.317 -        } else {
  13.318 -            char guid[64];
  13.319 -            SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick);
  13.320 -            SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick),
  13.321 -                                      guid, sizeof (guid));
  13.322 -            switch (SDL_JoystickGetType(joystick)) {
  13.323 -            case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
  13.324 -                type = "Game Controller";
  13.325 -                break;
  13.326 -            case SDL_JOYSTICK_TYPE_WHEEL:
  13.327 -                type = "Wheel";
  13.328 -                break;
  13.329 -            case SDL_JOYSTICK_TYPE_ARCADE_STICK:
  13.330 -                type = "Arcade Stick";
  13.331 -                break;
  13.332 -            case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
  13.333 -                type = "Flight Stick";
  13.334 -                break;
  13.335 -            case SDL_JOYSTICK_TYPE_DANCE_PAD:
  13.336 -                type = "Dance Pad";
  13.337 -                break;
  13.338 -            case SDL_JOYSTICK_TYPE_GUITAR:
  13.339 -                type = "Guitar";
  13.340 -                break;
  13.341 -            case SDL_JOYSTICK_TYPE_DRUM_KIT:
  13.342 -                type = "Drum Kit";
  13.343 -                break;
  13.344 -            case SDL_JOYSTICK_TYPE_ARCADE_PAD:
  13.345 -                type = "Arcade Pad";
  13.346 -                break;
  13.347 -            case SDL_JOYSTICK_TYPE_THROTTLE:
  13.348 -                type = "Throttle";
  13.349 -                break;
  13.350 -            default:
  13.351 -                type = "Unknown";
  13.352 -                break;
  13.353 -            }
  13.354 -            SDL_Log("       type: %s\n", type);
  13.355 -            SDL_Log("       axes: %d\n", SDL_JoystickNumAxes(joystick));
  13.356 -            SDL_Log("      balls: %d\n", SDL_JoystickNumBalls(joystick));
  13.357 -            SDL_Log("       hats: %d\n", SDL_JoystickNumHats(joystick));
  13.358 -            SDL_Log("    buttons: %d\n", SDL_JoystickNumButtons(joystick));
  13.359 -            SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
  13.360 -            SDL_Log("       guid: %s\n", guid);
  13.361 -            SDL_Log("    VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
  13.362 -            SDL_JoystickClose(joystick);
  13.363 -        }
  13.364 -    }
  13.365 -
  13.366 -#if defined(__ANDROID__) || defined(__IPHONEOS__)
  13.367 -    if (SDL_NumJoysticks() > 0) {
  13.368 -#else
  13.369 -    if (argv[1]) {
  13.370 -#endif
  13.371 -        SDL_bool reportederror = SDL_FALSE;
  13.372 -        SDL_bool keepGoing = SDL_TRUE;
  13.373 -        SDL_Event event;
  13.374 -        int device;
  13.375 -#if defined(__ANDROID__) || defined(__IPHONEOS__)
  13.376 -        device = 0;
  13.377 -#else
  13.378 -        device = atoi(argv[1]);
  13.379 -#endif
  13.380 -        joystick = SDL_JoystickOpen(device);
  13.381 -        if (joystick != NULL) {
  13.382 -            SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick);
  13.383 -        }
  13.384 -
  13.385 -        while ( keepGoing ) {
  13.386 -            if (joystick == NULL) {
  13.387 -                if ( !reportederror ) {
  13.388 -                    SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
  13.389 -                    keepGoing = SDL_FALSE;
  13.390 -                    reportederror = SDL_TRUE;
  13.391 -                }
  13.392 -            } else {
  13.393 -                reportederror = SDL_FALSE;
  13.394 -                keepGoing = WatchJoystick(joystick);
  13.395 -                SDL_JoystickClose(joystick);
  13.396 -            }
  13.397 -
  13.398 -            joystick = NULL;
  13.399 -            if (keepGoing) {
  13.400 -                SDL_Log("Waiting for attach\n");
  13.401 -            }
  13.402 -            while (keepGoing) {
  13.403 -                SDL_WaitEvent(&event);
  13.404 -                if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN)
  13.405 -                    || (event.type == SDL_MOUSEBUTTONDOWN)) {
  13.406 -                    keepGoing = SDL_FALSE;
  13.407 -                } else if (event.type == SDL_JOYDEVICEADDED) {
  13.408 -                    device = event.jdevice.which;
  13.409 -                    joystick = SDL_JoystickOpen(device);
  13.410 -                    if (joystick != NULL) {
  13.411 -                        SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick);
  13.412 -                    }
  13.413 -                    break;
  13.414 -                }
  13.415 -            }
  13.416 -        }
  13.417 -    }
  13.418      SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
  13.419  
  13.420      return 0;