From aba279289685be3fec7cfd820ae66e2d09479efd Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 18 Apr 2020 21:41:37 -0700 Subject: [PATCH] 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 | 3 + VisualC/SDL/SDL.vcxproj.filters | 3 + include/SDL_config_windows.h | 3 + src/joystick/SDL_gamecontroller.c | 2 +- src/joystick/SDL_gamecontrollerdb.h | 3 + src/joystick/SDL_joystick.c | 13 + src/joystick/SDL_joystick_c.h | 3 + src/joystick/SDL_sysjoystick.h | 1 + .../windows/SDL_windows_gaming_input.c | 705 ++++++++++++++++++ src/sensor/windows/SDL_windowssensor.c | 4 - test/controllermap.c | 74 +- test/testgamecontroller.c | 286 +++---- test/testjoystick.c | 361 ++++----- 13 files changed, 1043 insertions(+), 418 deletions(-) create mode 100644 src/joystick/windows/SDL_windows_gaming_input.c diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 1c968094f..701ad2b96 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -418,11 +418,13 @@ + + @@ -438,6 +440,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index cbf6db581..9f68a1629 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -484,6 +484,9 @@ + + + diff --git a/include/SDL_config_windows.h b/include/SDL_config_windows.h index 0f242d9ae..cce5af44c 100644 --- a/include/SDL_config_windows.h +++ b/include/SDL_config_windows.h @@ -198,6 +198,7 @@ typedef unsigned int uintptr_t; #define SDL_JOYSTICK_HIDAPI 1 #define SDL_JOYSTICK_RAWINPUT 1 #define SDL_JOYSTICK_VIRTUAL 1 +/*#define SDL_JOYSTICK_WGI 1*/ #define SDL_JOYSTICK_XINPUT 1 #define SDL_HAPTIC_DINPUT 1 #define SDL_HAPTIC_XINPUT 1 @@ -260,3 +261,5 @@ typedef unsigned int uintptr_t; #endif #endif /* SDL_config_windows_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 1468c427c..4dcbd5dac 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -1045,7 +1045,7 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForNameAndGUID(const } #endif /* __LINUX__ */ - if (!mapping && name) { + if (!mapping && name && !SDL_IsJoystickWGI(guid)) { if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX")) { mapping = s_pXInputMapping; } diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h index fb74cbb51..da78ab927 100644 --- a/src/joystick/SDL_gamecontrollerdb.h +++ b/src/joystick/SDL_gamecontrollerdb.h @@ -34,6 +34,9 @@ static const char *s_ControllerMappings [] = #if SDL_JOYSTICK_XINPUT "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,", #endif +#if SDL_JOYSTICK_WGI + "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,", +#endif #if SDL_JOYSTICK_DINPUT "03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,", "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,", diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 3ec854e95..e2bce51cf 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -58,6 +58,9 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = { #ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ &SDL_HIDAPI_JoystickDriver, #endif +#if defined(SDL_JOYSTICK_WGI) + &SDL_WGI_JoystickDriver, +#endif #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) &SDL_WINDOWS_JoystickDriver, #endif @@ -1731,6 +1734,12 @@ SDL_IsJoystickXInput(SDL_JoystickGUID guid) return (guid.data[14] == 'x') ? SDL_TRUE : SDL_FALSE; } +SDL_bool +SDL_IsJoystickWGI(SDL_JoystickGUID guid) +{ + return (guid.data[14] == 'w') ? SDL_TRUE : SDL_FALSE; +} + SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid) { @@ -1840,6 +1849,10 @@ static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_JoystickGUID guid) } } + if (SDL_IsJoystickWGI(guid)) { + return (SDL_JoystickType)guid.data[15]; + } + if (SDL_IsJoystickVirtual(guid)) { return (SDL_JoystickType)guid.data[15]; } diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 6a56cc4eb..392a5f807 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -70,6 +70,9 @@ extern SDL_bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_i /* Function to return whether a joystick guid comes from the XInput driver */ extern SDL_bool SDL_IsJoystickXInput(SDL_JoystickGUID guid); +/* Function to return whether a joystick guid comes from the WGI driver */ +extern SDL_bool SDL_IsJoystickWGI(SDL_JoystickGUID guid); + /* Function to return whether a joystick guid comes from the HIDAPI driver */ extern SDL_bool SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid); diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 9cb85cddd..e0ac96726 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -153,6 +153,7 @@ extern SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver; extern SDL_JoystickDriver SDL_IOS_JoystickDriver; extern SDL_JoystickDriver SDL_LINUX_JoystickDriver; extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver; +extern SDL_JoystickDriver SDL_WGI_JoystickDriver; extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver; #endif /* SDL_sysjoystick_h_ */ diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c new file mode 100644 index 000000000..c3854c1f8 --- /dev/null +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -0,0 +1,705 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_JOYSTICK_WGI + +#include "SDL_endian.h" +#include "SDL_events.h" +#include "../SDL_sysjoystick.h" + +#include "../../core/windows/SDL_windows.h" +#define COBJMACROS +#include "windows.gaming.input.h" + + +struct joystick_hwdata +{ + __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller; + __x_ABI_CWindows_CGaming_CInput_CIGameController *gamecontroller; + __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo *battery; + __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; + UINT64 timestamp; +}; + +typedef struct WindowsGamingInputControllerState { + SDL_JoystickID instance_id; + __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller; + char *name; + SDL_JoystickGUID guid; + SDL_JoystickType type; + int naxes; + int nhats; + int nbuttons; +} WindowsGamingInputControllerState; + +static struct { + __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics *statics; + __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics *arcade_stick_statics; + __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2 *arcade_stick_statics2; + __x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics *flight_stick_statics; + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics; + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *gamepad_statics2; + __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics *racing_wheel_statics; + __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2 *racing_wheel_statics2; + EventRegistrationToken controller_added_token; + EventRegistrationToken controller_removed_token; + int controller_count; + WindowsGamingInputControllerState *controllers; +} wgi; + +static const IID IID_IRawGameControllerStatics = { 0xEB8D0792, 0xE95A, 0x4B19, { 0xAF, 0xC7, 0x0A, 0x59, 0xF8, 0xBF, 0x75, 0x9E } }; +static const IID IID_IRawGameController = { 0x7CAD6D91, 0xA7E1, 0x4F71, { 0x9A, 0x78, 0x33, 0xE9, 0xC5, 0xDF, 0xEA, 0x62 } }; +static const IID IID_IRawGameController2 = { 0x43C0C035, 0xBB73, 0x4756, { 0xA7, 0x87, 0x3E, 0xD6, 0xBE, 0xA6, 0x17, 0xBD } }; +static const IID IID_IEventHandler_RawGameController = { 0x00621c22, 0x42e8, 0x529f, { 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72 } }; +static const IID IID_IGameController = { 0x1BAF6522, 0x5F64, 0x42C5, { 0x82, 0x67, 0xB9, 0xFE, 0x22, 0x15, 0xBF, 0xBD } }; +static const IID IID_IGameControllerBatteryInfo = { 0xDCECC681, 0x3963, 0x4DA6, { 0x95, 0x5D, 0x55, 0x3F, 0x3B, 0x6F, 0x61, 0x61 } }; +static const IID IID_IArcadeStickStatics = { 0x5C37B8C8, 0x37B1, 0x4AD8, { 0x94, 0x58, 0x20, 0x0F, 0x1A, 0x30, 0x01, 0x8E } }; +static const IID IID_IArcadeStickStatics2 = { 0x52B5D744, 0xBB86, 0x445A, { 0xB5, 0x9C, 0x59, 0x6F, 0x0E, 0x2A, 0x49, 0xDF } }; +static const IID IID_IArcadeStick = { 0xB14A539D, 0xBEFB, 0x4C81, { 0x80, 0x51, 0x15, 0xEC, 0xF3, 0xB1, 0x30, 0x36 } }; +static const IID IID_IFlightStickStatics = { 0x5514924A, 0xFECC, 0x435E, { 0x83, 0xDC, 0x5C, 0xEC, 0x8A, 0x18, 0xA5, 0x20 } }; +static const IID IID_IFlightStick = { 0xB4A2C01C, 0xB83B, 0x4459, { 0xA1, 0xA9, 0x97, 0xB0, 0x3C, 0x33, 0xDA, 0x7C } }; +static const IID IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } }; +static const IID IID_IGamepadStatics2 = { 0x42676DC5, 0x0856, 0x47C4, { 0x92, 0x13, 0xB3, 0x95, 0x50, 0x4C, 0x3A, 0x3C } }; +static const IID IID_IGamepad = { 0xBC7BB43C, 0x0A69, 0x3903, { 0x9E, 0x9D, 0xA5, 0x0F, 0x86, 0xA4, 0x5D, 0xE5 } }; +static const IID IID_IRacingWheelStatics = { 0x3AC12CD5, 0x581B, 0x4936, { 0x9F, 0x94, 0x69, 0xF1, 0xE6, 0x51, 0x4C, 0x7D } }; +static const IID IID_IRacingWheelStatics2 = { 0xE666BCAA, 0xEDFD, 0x4323, { 0xA9, 0xF6, 0x3C, 0x38, 0x40, 0x48, 0xD1, 0xED } }; +static const IID IID_IRacingWheel = { 0xF546656F, 0xE106, 0x4C82, { 0xA9, 0x0F, 0x55, 0x40, 0x12, 0x90, 0x4B, 0x85 } }; + +extern SDL_bool SDL_XINPUT_Enabled(void); + +static SDL_bool +SDL_IsXInputDevice(Uint16 vendor, Uint16 product) +{ + PRAWINPUTDEVICELIST raw_devices = NULL; + UINT i, raw_device_count = 0; + LONG vidpid = MAKELONG(vendor, product); + + if (!SDL_XINPUT_Enabled()) { + return SDL_FALSE; + } + + /* Go through RAWINPUT (WinXP and later) to find HID devices. */ + if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) { + return SDL_FALSE; /* oh well. */ + } + + raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count); + if (raw_devices == NULL) { + SDL_OutOfMemory(); + return SDL_FALSE; + } + + if (GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) { + SDL_free(raw_devices); + raw_devices = NULL; + return SDL_FALSE; /* oh well. */ + } + + for (i = 0; i < raw_device_count; i++) { + RID_DEVICE_INFO rdi; + char devName[128]; + UINT rdiSize = sizeof(rdi); + UINT nameSize = SDL_arraysize(devName); + + rdi.cbSize = sizeof(rdi); + if ((raw_devices[i].dwType == RIM_TYPEHID) && + (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) && + (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) && + (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) && + (SDL_strstr(devName, "IG_") != NULL)) { + return SDL_TRUE; + } + } + + return SDL_FALSE; +} + +static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_QueryInterface(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This, REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_INVALIDARG; + } + + *ppvObject = NULL; + if (WIN_IsEqualIID(riid, &IID_IUnknown) || WIN_IsEqualIID(riid, &IID_IEventHandler_RawGameController)) { + *ppvObject = This; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This) +{ + return 1; +} + +static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This) +{ + return 1; +} + +static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdded(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController * *e) +{ + HRESULT hr; + __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL; + + hr = IUnknown_QueryInterface((IUnknown *)e, &IID_IRawGameController, (void **)&controller); + if (SUCCEEDED(hr)) { + char *name = NULL; + SDL_JoystickGUID guid; + Uint16 vendor = 0; + Uint16 product = 0; + Uint16 version = 0; + SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN; + Uint16 *guid16 = (Uint16 *)guid.data; + __x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL; + __x_ABI_CWindows_CGaming_CInput_CIGameController *gamecontroller = NULL; + + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor); + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product); + + if (SDL_IsXInputDevice(vendor, product)) { + /* This will be handled by the XInput driver */ + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller); + return S_OK; + } + + hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID_IRawGameController2, (void **)&controller2); + if (SUCCEEDED(hr)) { + HSTRING hString; + + hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString); + if (SUCCEEDED(hr)) { + HMODULE hModule = LoadLibraryA("combase.dll"); + if (hModule != NULL) { + typedef PCWSTR (WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length); + + WindowsGetStringRawBuffer_t WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)GetProcAddress(hModule, "WindowsGetStringRawBuffer"); + if (WindowsGetStringRawBufferFunc) { + PCWSTR string = WindowsGetStringRawBufferFunc(hString, NULL); + if (string) { + name = WIN_StringToUTF8(string); + } + } + FreeLibrary(hModule); + } + } + __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2); + } + + if (SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID_IGameController, (void **)&gamecontroller))) { + __x_ABI_CWindows_CGaming_CInput_CIArcadeStick *arcade_stick = NULL; + __x_ABI_CWindows_CGaming_CInput_CIFlightStick *flight_stick = NULL; + __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad = NULL; + __x_ABI_CWindows_CGaming_CInput_CIRacingWheel *racing_wheel = NULL; + + if (wgi.gamepad_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, gamecontroller, &gamepad)) && gamepad) { + type = SDL_JOYSTICK_TYPE_GAMECONTROLLER; + __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad); + } else if (wgi.arcade_stick_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_FromGameController(wgi.arcade_stick_statics2, gamecontroller, &arcade_stick)) && arcade_stick) { + type = SDL_JOYSTICK_TYPE_ARCADE_STICK; + __x_ABI_CWindows_CGaming_CInput_CIArcadeStick_Release(arcade_stick); + } else if (wgi.flight_stick_statics && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_FromGameController(wgi.flight_stick_statics, gamecontroller, &flight_stick)) && flight_stick) { + type = SDL_JOYSTICK_TYPE_FLIGHT_STICK; + __x_ABI_CWindows_CGaming_CInput_CIFlightStick_Release(flight_stick); + } else if (wgi.racing_wheel_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_FromGameController(wgi.racing_wheel_statics2, gamecontroller, &racing_wheel)) && racing_wheel) { + type = SDL_JOYSTICK_TYPE_WHEEL; + __x_ABI_CWindows_CGaming_CInput_CIRacingWheel_Release(racing_wheel); + } + __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(gamecontroller); + } + + /* FIXME: Is there any way to tell whether this is a Bluetooth device? */ + *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB); + *guid16++ = 0; + *guid16++ = SDL_SwapLE16(vendor); + *guid16++ = 0; + *guid16++ = SDL_SwapLE16(product); + *guid16++ = 0; + *guid16++ = SDL_SwapLE16(version); + *guid16++ = 0; + + /* Note that this is a Windows Gaming Input device for special handling elsewhere */ + guid.data[14] = 'w'; + guid.data[15] = (Uint8)type; + + if (SDL_ShouldIgnoreJoystick(name, guid)) { + SDL_free(name); + } else { + /* New device, add it */ + WindowsGamingInputControllerState *controllers = SDL_realloc(wgi.controllers, sizeof(wgi.controllers[0]) * (wgi.controller_count + 1)); + if (controllers) { + WindowsGamingInputControllerState *state = &controllers[wgi.controller_count]; + SDL_JoystickID joystickID = SDL_GetNextJoystickInstanceID(); + + SDL_zerop(state); + state->instance_id = joystickID; + state->controller = controller; + state->name = name; + state->guid = guid; + state->type = type; + + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(controller, &state->nbuttons); + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(controller, &state->naxes); + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(controller, &state->nhats); + + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(controller); + + ++wgi.controller_count; + wgi.controllers = controllers; + + SDL_PrivateJoystickAdded(joystickID); + } + } + + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller); + } + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeRemoved(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController * This, IInspectable *sender, __x_ABI_CWindows_CGaming_CInput_CIRawGameController * *e) +{ + HRESULT hr; + __x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL; + + hr = IUnknown_QueryInterface((IUnknown *)e, &IID_IRawGameController, (void **)&controller); + if (SUCCEEDED(hr)) { + int i; + + for (i = 0; i < wgi.controller_count ; i++) { + if (wgi.controllers[i].controller == controller) { + WindowsGamingInputControllerState *state = &wgi.controllers[i]; + SDL_JoystickID joystickID = state->instance_id; + + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(state->controller); + + SDL_free(state->name); + + --wgi.controller_count; + if (i < wgi.controller_count) { + SDL_memmove(&wgi.controllers[i], &wgi.controllers[i + 1], (wgi.controller_count - i) * sizeof(wgi.controllers[i])); + } + + SDL_PrivateJoystickRemoved(joystickID); + break; + } + } + + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller); + } + return S_OK; +} + +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = { + IEventHandler_CRawGameControllerVtbl_QueryInterface, + IEventHandler_CRawGameControllerVtbl_AddRef, + IEventHandler_CRawGameControllerVtbl_Release, + IEventHandler_CRawGameControllerVtbl_InvokeAdded +}; +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController controller_added = { + &controller_added_vtbl +}; + +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = { + IEventHandler_CRawGameControllerVtbl_QueryInterface, + IEventHandler_CRawGameControllerVtbl_AddRef, + IEventHandler_CRawGameControllerVtbl_Release, + IEventHandler_CRawGameControllerVtbl_InvokeRemoved +}; +static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameController controller_removed = { + &controller_removed_vtbl +}; + +static int +WGI_JoystickInit(void) +{ + if (FAILED(WIN_CoInitialize())) { + return SDL_SetError("CoInitialize() failed"); + } + + HRESULT hr; + HMODULE hModule = LoadLibraryA("combase.dll"); + if (hModule != NULL) { + typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string); + typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string); + typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory); + + WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString"); + WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString"); + RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory"); + if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) { + LPTSTR pNamespace; + HSTRING hNamespaceString; + + pNamespace = L"Windows.Gaming.Input.RawGameController"; + hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); + if (SUCCEEDED(hr)) { + hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IRawGameControllerStatics, &wgi.statics); + if (!SUCCEEDED(hr)) { + SDL_SetError("Couldn't find IRawGameControllerStatics: 0x%x", hr); + } + WindowsDeleteStringFunc(hNamespaceString); + } + + pNamespace = L"Windows.Gaming.Input.ArcadeStick"; + hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); + if (SUCCEEDED(hr)) { + hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IArcadeStickStatics, &wgi.arcade_stick_statics); + if (SUCCEEDED(hr)) { + __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_QueryInterface(wgi.arcade_stick_statics, &IID_IArcadeStickStatics2, &wgi.arcade_stick_statics2); + } else { + SDL_SetError("Couldn't find IID_IArcadeStickStatics: 0x%x", hr); + } + WindowsDeleteStringFunc(hNamespaceString); + } + + pNamespace = L"Windows.Gaming.Input.FlightStick"; + hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); + if (SUCCEEDED(hr)) { + hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IFlightStickStatics, &wgi.flight_stick_statics); + if (!SUCCEEDED(hr)) { + SDL_SetError("Couldn't find IID_IFlightStickStatics: 0x%x", hr); + } + WindowsDeleteStringFunc(hNamespaceString); + } + + pNamespace = L"Windows.Gaming.Input.Gamepad"; + hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); + if (SUCCEEDED(hr)) { + hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IGamepadStatics, &wgi.gamepad_statics); + if (SUCCEEDED(hr)) { + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_QueryInterface(wgi.gamepad_statics, &IID_IGamepadStatics2, &wgi.gamepad_statics2); + } else { + SDL_SetError("Couldn't find IGamepadStatics: 0x%x", hr); + } + WindowsDeleteStringFunc(hNamespaceString); + } + + pNamespace = L"Windows.Gaming.Input.RacingWheel"; + hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); + if (SUCCEEDED(hr)) { + hr = RoGetActivationFactoryFunc(hNamespaceString, &IID_IRacingWheelStatics, &wgi.racing_wheel_statics); + if (SUCCEEDED(hr)) { + __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_QueryInterface(wgi.racing_wheel_statics, &IID_IRacingWheelStatics2, &wgi.racing_wheel_statics2); + } else { + SDL_SetError("Couldn't find IRacingWheelStatics: 0x%x", hr); + } + WindowsDeleteStringFunc(hNamespaceString); + } + } + FreeLibrary(hModule); + } + + if (wgi.statics) { + hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.statics, &controller_added, &wgi.controller_added_token); + if (!SUCCEEDED(hr)) { + SDL_SetError("add_RawGameControllerAdded() failed: 0x%x\n", hr); + } + + hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.statics, &controller_removed, &wgi.controller_removed_token); + if (!SUCCEEDED(hr)) { + SDL_SetError("add_RawGameControllerRemoved() failed: 0x%x\n", hr); + } + } + + return 0; +} + +static int +WGI_JoystickGetCount(void) +{ + return wgi.controller_count; +} + +static void +WGI_JoystickDetect(void) +{ +} + +static const char * +WGI_JoystickGetDeviceName(int device_index) +{ + return wgi.controllers[device_index].name; +} + +static int +WGI_JoystickGetDevicePlayerIndex(int device_index) +{ + return -1; +} + +static void +WGI_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ +} + +static SDL_JoystickGUID +WGI_JoystickGetDeviceGUID(int device_index) +{ + return wgi.controllers[device_index].guid; +} + +static SDL_JoystickID +WGI_JoystickGetDeviceInstanceID(int device_index) +{ + return wgi.controllers[device_index].instance_id; +} + +static int +WGI_JoystickOpen(SDL_Joystick * joystick, int device_index) +{ + WindowsGamingInputControllerState *state = &wgi.controllers[device_index]; + struct joystick_hwdata *hwdata; + boolean wireless = SDL_FALSE; + + hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); + if (!hwdata) { + return SDL_OutOfMemory(); + } + joystick->hwdata = hwdata; + + hwdata->controller = state->controller; + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_AddRef(hwdata->controller); + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID_IGameController, (void **)&hwdata->gamecontroller); + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID_IGameControllerBatteryInfo, (void **)&hwdata->battery); + + if (wgi.gamepad_statics2) { + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->gamecontroller, &hwdata->gamepad); + } + + if (hwdata->gamecontroller) { + __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->gamecontroller, &wireless); + } + + /* Initialize the joystick capabilities */ + joystick->nbuttons = state->nbuttons; + joystick->naxes = state->naxes; + joystick->nhats = state->nhats; + joystick->epowerlevel = wireless ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED; + + if (wireless && hwdata->battery) { + HRESULT hr; + __x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report; + + hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report); + if (SUCCEEDED(hr)) { + int full_capacity = 0, curr_capacity = 0; + __FIReference_1_int *full_capacityP, *curr_capacityP; + + hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_FullChargeCapacityInMilliwattHours(report, &full_capacityP); + if (SUCCEEDED(hr)) { + __FIReference_1_int_get_Value(full_capacityP, &full_capacity); + __FIReference_1_int_Release(full_capacityP); + } + + hr = __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_get_RemainingCapacityInMilliwattHours(report, &curr_capacityP); + if (SUCCEEDED(hr)) { + __FIReference_1_int_get_Value(curr_capacityP, &curr_capacity); + __FIReference_1_int_Release(curr_capacityP); + } + + if (full_capacity > 0) { + float ratio = (float)curr_capacity / full_capacity; + + if (ratio <= 0.05f) { + joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY; + } else if (ratio <= 0.20f) { + joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW; + } else if (ratio <= 0.70f) { + joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM; + } else { + joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; + } + } + __x_ABI_CWindows_CDevices_CPower_CIBatteryReport_Release(report); + } + } + return 0; +} + +static int +WGI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + struct joystick_hwdata *hwdata = joystick->hwdata; + + if (hwdata->gamepad) { + HRESULT hr; + struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration; + + SDL_zero(vibration); + vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16; + vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16; + hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, vibration); + if (SUCCEEDED(hr)) { + return 0; + } else { + return SDL_SetError("Setting vibration failed: 0x%x\n", hr); + } + } else { + return SDL_Unsupported(); + } +} + +static Uint8 +ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value) +{ + switch (value) { + case GameControllerSwitchPosition_Up: + return SDL_HAT_UP; + case GameControllerSwitchPosition_UpRight: + return SDL_HAT_RIGHTUP; + case GameControllerSwitchPosition_Right: + return SDL_HAT_RIGHT; + case GameControllerSwitchPosition_DownRight: + return SDL_HAT_RIGHTDOWN; + case GameControllerSwitchPosition_Down: + return SDL_HAT_DOWN; + case GameControllerSwitchPosition_DownLeft: + return SDL_HAT_LEFTDOWN; + case GameControllerSwitchPosition_Left: + return SDL_HAT_LEFT; + case GameControllerSwitchPosition_UpLeft: + return SDL_HAT_LEFTUP; + default: + return SDL_HAT_CENTERED; + } +} + +static void +WGI_JoystickUpdate(SDL_Joystick * joystick) +{ + struct joystick_hwdata *hwdata = joystick->hwdata; + HRESULT hr; + UINT32 nbuttons = joystick->nbuttons; + boolean *buttons = SDL_stack_alloc(boolean, nbuttons); + UINT32 nhats = joystick->nhats; + __x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats); + UINT32 naxes = joystick->naxes; + DOUBLE *axes = SDL_stack_alloc(DOUBLE, naxes); + UINT64 timestamp; + + hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, ×tamp); + if (SUCCEEDED(hr) && timestamp != hwdata->timestamp) { + UINT32 i; + + for (i = 0; i < nbuttons; ++i) { + SDL_PrivateJoystickButton(joystick, i, buttons[i]); + } + for (i = 0; i < nhats; ++i) { + SDL_PrivateJoystickHat(joystick, i, ConvertHatValue(hats[i])); + } + for (i = 0; i < naxes; ++i) { + SDL_PrivateJoystickAxis(joystick, i, (int)(axes[i] * 65535) - 32768); + } + hwdata->timestamp = timestamp; + } + + SDL_stack_free(buttons); + SDL_stack_free(hats); + SDL_stack_free(axes); +} + +static void +WGI_JoystickClose(SDL_Joystick * joystick) +{ + struct joystick_hwdata *hwdata = joystick->hwdata; + + if (hwdata) { + if (hwdata->controller) { + __x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(hwdata->controller); + } + if (hwdata->gamecontroller) { + __x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->gamecontroller); + } + if (hwdata->battery) { + __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_Release(hwdata->battery); + } + if (hwdata->gamepad) { + __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(hwdata->gamepad); + } + SDL_free(hwdata); + } + joystick->hwdata = NULL; +} + +static void +WGI_JoystickQuit(void) +{ + if (wgi.statics) { + while (wgi.controller_count > 0) { + IEventHandler_CRawGameControllerVtbl_InvokeRemoved(&controller_removed, NULL, (__x_ABI_CWindows_CGaming_CInput_CIRawGameController **)wgi.controllers[wgi.controller_count - 1].controller); + } + if (wgi.controllers) { + SDL_free(wgi.controllers); + } + + if (wgi.arcade_stick_statics) { + __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_Release(wgi.arcade_stick_statics); + } + if (wgi.arcade_stick_statics2) { + __x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_Release(wgi.arcade_stick_statics2); + } + if (wgi.flight_stick_statics) { + __x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_Release(wgi.flight_stick_statics); + } + if (wgi.gamepad_statics) { + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi.gamepad_statics); + } + if (wgi.gamepad_statics2) { + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_Release(wgi.gamepad_statics2); + } + if (wgi.racing_wheel_statics) { + __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_Release(wgi.racing_wheel_statics); + } + if (wgi.racing_wheel_statics2) { + __x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_Release(wgi.racing_wheel_statics2); + } + + __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerAdded(wgi.statics, wgi.controller_added_token); + __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.statics, wgi.controller_removed_token); + __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.statics); + } + SDL_zero(wgi); + + WIN_CoUninitialize(); +} + +SDL_JoystickDriver SDL_WGI_JoystickDriver = +{ + WGI_JoystickInit, + WGI_JoystickGetCount, + WGI_JoystickDetect, + WGI_JoystickGetDeviceName, + WGI_JoystickGetDevicePlayerIndex, + WGI_JoystickSetDevicePlayerIndex, + WGI_JoystickGetDeviceGUID, + WGI_JoystickGetDeviceInstanceID, + WGI_JoystickOpen, + WGI_JoystickRumble, + WGI_JoystickUpdate, + WGI_JoystickClose, + WGI_JoystickQuit, +}; + +#endif /* SDL_JOYSTICK_WGI */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/sensor/windows/SDL_windowssensor.c b/src/sensor/windows/SDL_windowssensor.c index 53b592e4c..3fa1df968 100644 --- a/src/sensor/windows/SDL_windowssensor.c +++ b/src/sensor/windows/SDL_windowssensor.c @@ -41,10 +41,6 @@ DEFINE_GUID(IID_SensorManager, 0xBD77DB67, 0x45A8, 0x42DC, 0x8D, 0x00, 0x6D, 0xC DEFINE_GUID(IID_SensorManagerEvents, 0x9B3B0B86, 0x266A, 0x4AAD, 0xB2, 0x1F, 0xFD, 0xE5, 0x50, 0x10, 0x01, 0xB7); DEFINE_GUID(IID_SensorEvents, 0x5D8DCC91, 0x4641, 0x47E7, 0xB7, 0xC3, 0xB7, 0x4F, 0x48, 0xA6, 0xC3, 0x91); -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] -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] -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] - typedef struct { SDL_SensorID id; diff --git a/test/controllermap.c b/test/controllermap.c index ced930429..9e3ae32f5 100644 --- a/test/controllermap.c +++ b/test/controllermap.c @@ -155,6 +155,9 @@ static int s_iCurrentBinding; static Uint32 s_unPendingAdvanceTime; static SDL_bool s_bBindingComplete; +static SDL_Window *window; +static SDL_bool done = SDL_FALSE; + SDL_Texture * LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) { @@ -357,30 +360,18 @@ BMergeAxisBindings(int iIndex) static void WatchJoystick(SDL_Joystick * joystick) { - SDL_Window *window = NULL; SDL_Renderer *screen = NULL; SDL_Texture *background, *button, *axis, *marker; const char *name = NULL; - SDL_bool done = SDL_FALSE; SDL_Event event; SDL_Rect dst; Uint8 alpha=200, alpha_step = -1; Uint32 alpha_ticks = 0; SDL_JoystickID nJoystickID; - /* Create a window to display joystick axis position */ - window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, - SCREEN_HEIGHT, 0); - if (window == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); - return; - } - screen = SDL_CreateRenderer(window, -1, 0); if (screen == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); - SDL_DestroyWindow(window); return; } @@ -705,7 +696,6 @@ WatchJoystick(SDL_Joystick * joystick) s_arrAxisState = NULL; SDL_DestroyRenderer(screen); - SDL_DestroyWindow(window); } int @@ -724,6 +714,34 @@ main(int argc, char *argv[]) exit(1); } + /* Create a window to display joystick axis position */ + window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, + SCREEN_HEIGHT, 0); + if (window == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); + return 2; + } + + while (SDL_NumJoysticks() == 0) { + SDL_Event event; + + while (SDL_PollEvent(&event) > 0) { + switch (event.type) { + case SDL_KEYDOWN: + if ((event.key.keysym.sym != SDLK_ESCAPE)) { + break; + } + /* Fall through to signal quit */ + case SDL_QUIT: + done = SDL_TRUE; + break; + default: + break; + } + } + } + /* Print information about the joysticks */ SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks()); for (i = 0; i < SDL_NumJoysticks(); ++i) { @@ -748,28 +766,16 @@ main(int argc, char *argv[]) } } -#ifdef __ANDROID__ - if (SDL_NumJoysticks() > 0) { -#else - if (argv[1]) { -#endif - int device; -#ifdef __ANDROID__ - device = 0; -#else - device = atoi(argv[1]); -#endif - joystick = SDL_JoystickOpen(device); - if (joystick == NULL) { - SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError()); - } else { - WatchJoystick(joystick); - SDL_JoystickClose(joystick); - } - } - else { - SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt"); + joystick = SDL_JoystickOpen(0); + if (joystick == NULL) { + SDL_Log("Couldn't open joystick 0: %s\n", SDL_GetError()); + } else { + WatchJoystick(joystick); + SDL_JoystickClose(joystick); } + + SDL_DestroyWindow(window); + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); return 0; diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c index 9fd5eb504..5ecf0c1e5 100644 --- a/test/testgamecontroller.c +++ b/test/testgamecontroller.c @@ -61,10 +61,12 @@ static const struct { int x; int y; double angle; } axis_positions[] = { {375, -20, 0.0}, /* TRIGGERRIGHT */ }; +SDL_Window *window = NULL; SDL_Renderer *screen = NULL; SDL_bool retval = SDL_FALSE; SDL_bool done = SDL_FALSE; SDL_Texture *background, *button, *axis; +SDL_GameController *gamecontroller; static SDL_Texture * LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) @@ -94,12 +96,29 @@ LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) return texture; } +static void +UpdateWindowTitle() +{ + const char *name = SDL_GameControllerName(gamecontroller); + const char *basetitle = "Game Controller Test: "; + const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1; + char *title = (char *)SDL_malloc(titlelen); + + retval = SDL_FALSE; + done = SDL_FALSE; + + if (title) { + SDL_snprintf(title, titlelen, "%s%s", basetitle, name); + SDL_SetWindowTitle(window, title); + SDL_free(title); + } +} + void loop(void *arg) { SDL_Event event; int i; - SDL_GameController *gamecontroller = (SDL_GameController *)arg; /* blank screen, set up for drawing this frame. */ SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); @@ -108,6 +127,29 @@ loop(void *arg) while (SDL_PollEvent(&event)) { switch (event.type) { + case SDL_CONTROLLERDEVICEADDED: + SDL_Log("Game controller device %d added.\n", (int) event.cdevice.which); + if (!gamecontroller) { + gamecontroller = SDL_GameControllerOpen(event.cdevice.which); + if (gamecontroller) { + UpdateWindowTitle(); + } else { + SDL_Log("Couldn't open controller: %s\n", SDL_GetError()); + } + } + break; + + case SDL_CONTROLLERDEVICEREMOVED: + SDL_Log("Game controller device %d removed.\n", (int) event.cdevice.which); + if (gamecontroller && event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) { + SDL_GameControllerClose(gamecontroller); + gamecontroller = SDL_GameControllerOpen(0); + if (gamecontroller) { + UpdateWindowTitle(); + } + } + break; + case SDL_CONTROLLERAXISMOTION: SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value); break; @@ -128,42 +170,39 @@ loop(void *arg) } } - /* Update visual controller state */ - for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { - if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) { - const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 }; - SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE); + if (gamecontroller) { + /* Update visual controller state */ + for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { + if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) { + const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 }; + SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE); + } } - } - for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) { - const Sint16 deadzone = 8000; /* !!! FIXME: real deadzone */ - const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i)); - if (value < -deadzone) { - const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; - const double angle = axis_positions[i].angle; - SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); - } else if (value > deadzone) { - const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; - const double angle = axis_positions[i].angle + 180.0; - SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); + for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) { + const Sint16 deadzone = 8000; /* !!! FIXME: real deadzone */ + const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i)); + if (value < -deadzone) { + const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; + const double angle = axis_positions[i].angle; + SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); + } else if (value > deadzone) { + const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 }; + const double angle = axis_positions[i].angle + 180.0; + SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE); + } } - } - /* Update rumble based on trigger state */ - { - Uint16 low_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) * 2; - Uint16 high_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * 2; - SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250); + /* Update rumble based on trigger state */ + { + Uint16 low_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) * 2; + Uint16 high_frequency_rumble = SDL_GameControllerGetAxis(gamecontroller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) * 2; + SDL_GameControllerRumble(gamecontroller, low_frequency_rumble, high_frequency_rumble, 250); + } } SDL_RenderPresent(screen); - if (!SDL_GameControllerGetAttached(gamecontroller)) { - done = SDL_TRUE; - retval = SDL_TRUE; /* keep going, wait for reattach. */ - } - #ifdef __EMSCRIPTEN__ if (done) { emscripten_cancel_main_loop(); @@ -171,92 +210,12 @@ loop(void *arg) #endif } -SDL_bool -WatchGameController(SDL_GameController * gamecontroller) -{ - const char *name = SDL_GameControllerName(gamecontroller); - const char *basetitle = "Game Controller Test: "; - const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1; - char *title = (char *)SDL_malloc(titlelen); - SDL_Window *window = NULL; - - retval = SDL_FALSE; - done = SDL_FALSE; - - if (title) { - SDL_snprintf(title, titlelen, "%s%s", basetitle, name); - } - - /* Create a window to display controller state */ - window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, - SCREEN_HEIGHT, 0); - SDL_free(title); - title = NULL; - if (window == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); - return SDL_FALSE; - } - - screen = SDL_CreateRenderer(window, -1, 0); - if (screen == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); - SDL_DestroyWindow(window); - return SDL_FALSE; - } - - SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); - SDL_RenderClear(screen); - SDL_RenderPresent(screen); - SDL_RaiseWindow(window); - - /* scale for platforms that don't give you the window size you asked for. */ - SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); - - background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); - button = LoadTexture(screen, "button.bmp", SDL_TRUE); - axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); - - if (!background || !button || !axis) { - SDL_DestroyRenderer(screen); - SDL_DestroyWindow(window); - return SDL_FALSE; - } - SDL_SetTextureColorMod(button, 10, 255, 21); - SDL_SetTextureColorMod(axis, 10, 255, 21); - - /* !!! FIXME: */ - /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/ - - /* Print info about the controller we are watching */ - SDL_Log("Watching controller %s\n", name ? name : "Unknown Controller"); - - /* Loop, getting controller events! */ -#ifdef __EMSCRIPTEN__ - emscripten_set_main_loop_arg(loop, gamecontroller, 0, 1); -#else - while (!done) { - loop(gamecontroller); - } -#endif - - SDL_DestroyRenderer(screen); - screen = NULL; - background = NULL; - button = NULL; - axis = NULL; - SDL_DestroyWindow(window); - return retval; -} - int main(int argc, char *argv[]) { int i; int nController = 0; - int retcode = 0; char guid[64]; - SDL_GameController *gamecontroller; /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); @@ -270,7 +229,7 @@ main(int argc, char *argv[]) SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); /* Print information about the mappings */ - if (!argv[1]) { + if (argv[1] && SDL_strcmp(argv[1], "--mappings") == 0) { SDL_Log("Supported mappings:\n"); for (i = 0; i < SDL_GameControllerNumMappings(); ++i) { char *mapping = SDL_GameControllerMappingForIndex(i); @@ -290,8 +249,7 @@ main(int argc, char *argv[]) SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), guid, sizeof (guid)); - if ( SDL_IsGameController(i) ) - { + if ( SDL_IsGameController(i) ) { nController++; name = SDL_GameControllerNameForIndex(i); switch (SDL_GameControllerTypeForIndex(i)) { @@ -327,62 +285,62 @@ main(int argc, char *argv[]) } SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks()); - if (argv[1]) { - SDL_bool reportederror = SDL_FALSE; - SDL_bool keepGoing = SDL_TRUE; - SDL_Event event; - int device = atoi(argv[1]); - if (device >= SDL_NumJoysticks()) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%i is an invalid joystick index.\n", device); - retcode = 1; - } else { - SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(device), - guid, sizeof (guid)); - SDL_Log("Attempting to open device %i, guid %s\n", device, guid); - gamecontroller = SDL_GameControllerOpen(device); - - if (gamecontroller != NULL) { - SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller); - } - - while (keepGoing) { - if (gamecontroller == NULL) { - if (!reportederror) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open gamecontroller %d: %s\n", device, SDL_GetError()); - retcode = 1; - keepGoing = SDL_FALSE; - reportederror = SDL_TRUE; - } - } else { - reportederror = SDL_FALSE; - keepGoing = WatchGameController(gamecontroller); - SDL_GameControllerClose(gamecontroller); - } - - gamecontroller = NULL; - if (keepGoing) { - SDL_Log("Waiting for attach\n"); - } - while (keepGoing) { - SDL_WaitEvent(&event); - if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN) - || (event.type == SDL_MOUSEBUTTONDOWN)) { - keepGoing = SDL_FALSE; - } else if (event.type == SDL_CONTROLLERDEVICEADDED) { - gamecontroller = SDL_GameControllerOpen(event.cdevice.which); - if (gamecontroller != NULL) { - SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller); - } - break; - } - } - } - } + /* Create a window to display controller state */ + window = SDL_CreateWindow("Game Controller Test", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, + SCREEN_HEIGHT, 0); + if (window == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); + return 2; } + screen = SDL_CreateRenderer(window, -1, 0); + if (screen == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); + SDL_DestroyWindow(window); + return 2; + } + + SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); + SDL_RenderClear(screen); + SDL_RenderPresent(screen); + + /* scale for platforms that don't give you the window size you asked for. */ + SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); + + background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); + button = LoadTexture(screen, "button.bmp", SDL_TRUE); + axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); + + if (!background || !button || !axis) { + SDL_DestroyRenderer(screen); + SDL_DestroyWindow(window); + return 2; + } + SDL_SetTextureColorMod(button, 10, 255, 21); + SDL_SetTextureColorMod(axis, 10, 255, 21); + + /* !!! FIXME: */ + /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/ + + /* Loop, getting controller events! */ +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop_arg(loop, NULL, 0, 1); +#else + while (!done) { + loop(NULL); + } +#endif + + SDL_DestroyRenderer(screen); + screen = NULL; + background = NULL; + button = NULL; + axis = NULL; + SDL_DestroyWindow(window); SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); - return retcode; + return 0; } #else diff --git a/test/testjoystick.c b/test/testjoystick.c index dea08bb4f..2d8d2e1bc 100644 --- a/test/testjoystick.c +++ b/test/testjoystick.c @@ -32,9 +32,62 @@ #define SCREEN_HEIGHT 480 #endif -SDL_Renderer *screen = NULL; -SDL_bool retval = SDL_FALSE; -SDL_bool done = SDL_FALSE; +static SDL_Window *window = NULL; +static SDL_Renderer *screen = NULL; +static SDL_Joystick *joystick = NULL; +static SDL_bool done = SDL_FALSE; + +static void +PrintJoystick(SDL_Joystick *joystick) +{ + const char *type; + char guid[64]; + + SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick); + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guid, sizeof (guid)); + switch (SDL_JoystickGetType(joystick)) { + case SDL_JOYSTICK_TYPE_GAMECONTROLLER: + type = "Game Controller"; + break; + case SDL_JOYSTICK_TYPE_WHEEL: + type = "Wheel"; + break; + case SDL_JOYSTICK_TYPE_ARCADE_STICK: + type = "Arcade Stick"; + break; + case SDL_JOYSTICK_TYPE_FLIGHT_STICK: + type = "Flight Stick"; + break; + case SDL_JOYSTICK_TYPE_DANCE_PAD: + type = "Dance Pad"; + break; + case SDL_JOYSTICK_TYPE_GUITAR: + type = "Guitar"; + break; + case SDL_JOYSTICK_TYPE_DRUM_KIT: + type = "Drum Kit"; + break; + case SDL_JOYSTICK_TYPE_ARCADE_PAD: + type = "Arcade Pad"; + break; + case SDL_JOYSTICK_TYPE_THROTTLE: + type = "Throttle"; + break; + default: + type = "Unknown"; + break; + } + SDL_Log("Joystick\n"); + SDL_Log(" name: %s\n", SDL_JoystickName(joystick)); + SDL_Log(" type: %s\n", type); + SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick)); + SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick)); + SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick)); + SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); + SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick)); + SDL_Log(" guid: %s\n", guid); + SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); +} static void DrawRect(SDL_Renderer *r, const int x, const int y, const int w, const int h) @@ -48,72 +101,89 @@ loop(void *arg) { SDL_Event event; int i; - SDL_Joystick *joystick = (SDL_Joystick *)arg; - /* blank screen, set up for drawing this frame. */ + /* blank screen, set up for drawing this frame. */ SDL_SetRenderDrawColor(screen, 0x0, 0x0, 0x0, SDL_ALPHA_OPAQUE); - SDL_RenderClear(screen); + SDL_RenderClear(screen); - while (SDL_PollEvent(&event)) { - switch (event.type) { + while (SDL_PollEvent(&event)) { + switch (event.type) { - case SDL_JOYDEVICEREMOVED: - SDL_Log("Joystick device %d removed.\n", (int) event.jdevice.which); - SDL_Log("Our instance ID is %d\n", (int) SDL_JoystickInstanceID(joystick)); - break; - - case SDL_JOYAXISMOTION: - SDL_Log("Joystick %d axis %d value: %d\n", - event.jaxis.which, - event.jaxis.axis, event.jaxis.value); - break; - case SDL_JOYHATMOTION: - SDL_Log("Joystick %d hat %d value:", - event.jhat.which, event.jhat.hat); - if (event.jhat.value == SDL_HAT_CENTERED) - SDL_Log(" centered"); - if (event.jhat.value & SDL_HAT_UP) - SDL_Log(" up"); - if (event.jhat.value & SDL_HAT_RIGHT) - SDL_Log(" right"); - if (event.jhat.value & SDL_HAT_DOWN) - SDL_Log(" down"); - if (event.jhat.value & SDL_HAT_LEFT) - SDL_Log(" left"); - SDL_Log("\n"); - break; - case SDL_JOYBALLMOTION: - SDL_Log("Joystick %d ball %d delta: (%d,%d)\n", - event.jball.which, - event.jball.ball, event.jball.xrel, event.jball.yrel); - break; - case SDL_JOYBUTTONDOWN: - SDL_Log("Joystick %d button %d down\n", - event.jbutton.which, event.jbutton.button); - /* First button triggers a 0.5 second full strength rumble */ - if (event.jbutton.button == 0) { - SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500); + case SDL_JOYDEVICEADDED: + SDL_Log("Joystick device %d added.\n", (int) event.jdevice.which); + if (!joystick) { + joystick = SDL_JoystickOpen(event.jdevice.which); + if (joystick) { + PrintJoystick(joystick); + } else { + SDL_Log("Couldn't open joystick: %s\n", SDL_GetError()); } - break; - case SDL_JOYBUTTONUP: - SDL_Log("Joystick %d button %d up\n", - event.jbutton.which, event.jbutton.button); - break; - case SDL_KEYDOWN: - if ((event.key.keysym.sym != SDLK_ESCAPE) && - (event.key.keysym.sym != SDLK_AC_BACK)) { - break; - } - /* Fall through to signal quit */ - case SDL_FINGERDOWN: - case SDL_MOUSEBUTTONDOWN: - case SDL_QUIT: - done = SDL_TRUE; - break; - default: + } + break; + + case SDL_JOYDEVICEREMOVED: + SDL_Log("Joystick device %d removed.\n", (int) event.jdevice.which); + if (event.jdevice.which == SDL_JoystickInstanceID(joystick)) { + SDL_JoystickClose(joystick); + joystick = SDL_JoystickOpen(0); + } + break; + + case SDL_JOYAXISMOTION: + SDL_Log("Joystick %d axis %d value: %d\n", + event.jaxis.which, + event.jaxis.axis, event.jaxis.value); + break; + case SDL_JOYHATMOTION: + SDL_Log("Joystick %d hat %d value:", + event.jhat.which, event.jhat.hat); + if (event.jhat.value == SDL_HAT_CENTERED) + SDL_Log(" centered"); + if (event.jhat.value & SDL_HAT_UP) + SDL_Log(" up"); + if (event.jhat.value & SDL_HAT_RIGHT) + SDL_Log(" right"); + if (event.jhat.value & SDL_HAT_DOWN) + SDL_Log(" down"); + if (event.jhat.value & SDL_HAT_LEFT) + SDL_Log(" left"); + SDL_Log("\n"); + break; + case SDL_JOYBALLMOTION: + SDL_Log("Joystick %d ball %d delta: (%d,%d)\n", + event.jball.which, + event.jball.ball, event.jball.xrel, event.jball.yrel); + break; + case SDL_JOYBUTTONDOWN: + SDL_Log("Joystick %d button %d down\n", + event.jbutton.which, event.jbutton.button); + /* First button triggers a 0.5 second full strength rumble */ + if (event.jbutton.button == 0) { + SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500); + } + break; + case SDL_JOYBUTTONUP: + SDL_Log("Joystick %d button %d up\n", + event.jbutton.which, event.jbutton.button); + break; + case SDL_KEYDOWN: + if ((event.key.keysym.sym != SDLK_ESCAPE) && + (event.key.keysym.sym != SDLK_AC_BACK)) { break; } + /* Fall through to signal quit */ + case SDL_FINGERDOWN: + case SDL_MOUSEBUTTONDOWN: + case SDL_QUIT: + done = SDL_TRUE; + break; + default: + break; } + } + + if (joystick) { + /* Update visual joystick state */ SDL_SetRenderDrawColor(screen, 0x00, 0xFF, 0x00, SDL_ALPHA_OPAQUE); for (i = 0; i < SDL_JoystickNumButtons(joystick); ++i) { @@ -172,13 +242,9 @@ loop(void *arg) DrawRect(screen, x, y, 8, 8); } + } - SDL_RenderPresent(screen); - - if (SDL_JoystickGetAttached( joystick ) == 0) { - done = SDL_TRUE; - retval = SDL_TRUE; /* keep going, wait for reattach. */ - } + SDL_RenderPresent(screen); #ifdef __EMSCRIPTEN__ if (done) { @@ -187,14 +253,19 @@ loop(void *arg) #endif } -static SDL_bool -WatchJoystick(SDL_Joystick * joystick) +int +main(int argc, char *argv[]) { - SDL_Window *window = NULL; - const char *name = NULL; + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); - retval = SDL_FALSE; - done = SDL_FALSE; + /* Enable standard application logging */ + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + /* Initialize SDL (Note: video is required to start event loop) */ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } /* Create a window to display joystick axis position */ window = SDL_CreateWindow("Joystick Test", SDL_WINDOWPOS_CENTERED, @@ -215,159 +286,19 @@ WatchJoystick(SDL_Joystick * joystick) SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE); SDL_RenderClear(screen); SDL_RenderPresent(screen); - SDL_RaiseWindow(window); - - /* Print info about the joystick we are watching */ - name = SDL_JoystickName(joystick); - SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick), - name ? name : "Unknown Joystick"); - SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n", - SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), - SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick)); /* Loop, getting joystick events! */ #ifdef __EMSCRIPTEN__ - emscripten_set_main_loop_arg(loop, joystick, 0, 1); + emscripten_set_main_loop_arg(loop, NULL, 0, 1); #else while (!done) { - loop(joystick); + loop(NULL); } #endif SDL_DestroyRenderer(screen); - screen = NULL; SDL_DestroyWindow(window); - return retval; -} -int -main(int argc, char *argv[]) -{ - const char *name, *type; - int i; - SDL_Joystick *joystick; - - SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); - - /* Enable standard application logging */ - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - - /* Initialize SDL (Note: video is required to start event loop) */ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); - exit(1); - } - - /* Print information about the joysticks */ - SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks()); - for (i = 0; i < SDL_NumJoysticks(); ++i) { - name = SDL_JoystickNameForIndex(i); - SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick"); - joystick = SDL_JoystickOpen(i); - if (joystick == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i, - SDL_GetError()); - } else { - char guid[64]; - SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick); - SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), - guid, sizeof (guid)); - switch (SDL_JoystickGetType(joystick)) { - case SDL_JOYSTICK_TYPE_GAMECONTROLLER: - type = "Game Controller"; - break; - case SDL_JOYSTICK_TYPE_WHEEL: - type = "Wheel"; - break; - case SDL_JOYSTICK_TYPE_ARCADE_STICK: - type = "Arcade Stick"; - break; - case SDL_JOYSTICK_TYPE_FLIGHT_STICK: - type = "Flight Stick"; - break; - case SDL_JOYSTICK_TYPE_DANCE_PAD: - type = "Dance Pad"; - break; - case SDL_JOYSTICK_TYPE_GUITAR: - type = "Guitar"; - break; - case SDL_JOYSTICK_TYPE_DRUM_KIT: - type = "Drum Kit"; - break; - case SDL_JOYSTICK_TYPE_ARCADE_PAD: - type = "Arcade Pad"; - break; - case SDL_JOYSTICK_TYPE_THROTTLE: - type = "Throttle"; - break; - default: - type = "Unknown"; - break; - } - SDL_Log(" type: %s\n", type); - SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick)); - SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick)); - SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick)); - SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); - SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick)); - SDL_Log(" guid: %s\n", guid); - SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); - SDL_JoystickClose(joystick); - } - } - -#if defined(__ANDROID__) || defined(__IPHONEOS__) - if (SDL_NumJoysticks() > 0) { -#else - if (argv[1]) { -#endif - SDL_bool reportederror = SDL_FALSE; - SDL_bool keepGoing = SDL_TRUE; - SDL_Event event; - int device; -#if defined(__ANDROID__) || defined(__IPHONEOS__) - device = 0; -#else - device = atoi(argv[1]); -#endif - joystick = SDL_JoystickOpen(device); - if (joystick != NULL) { - SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick); - } - - while ( keepGoing ) { - if (joystick == NULL) { - if ( !reportederror ) { - SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError()); - keepGoing = SDL_FALSE; - reportederror = SDL_TRUE; - } - } else { - reportederror = SDL_FALSE; - keepGoing = WatchJoystick(joystick); - SDL_JoystickClose(joystick); - } - - joystick = NULL; - if (keepGoing) { - SDL_Log("Waiting for attach\n"); - } - while (keepGoing) { - SDL_WaitEvent(&event); - if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN) - || (event.type == SDL_MOUSEBUTTONDOWN)) { - keepGoing = SDL_FALSE; - } else if (event.type == SDL_JOYDEVICEADDED) { - device = event.jdevice.which; - joystick = SDL_JoystickOpen(device); - if (joystick != NULL) { - SDL_assert(SDL_JoystickFromInstanceID(SDL_JoystickInstanceID(joystick)) == joystick); - } - break; - } - } - } - } SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); return 0;