996 lines
40 KiB
C
996 lines
40 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
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_sysjoystick.h"
|
|
#include "../hidapi/SDL_hidapijoystick_c.h"
|
|
#include "SDL_rawinputjoystick_c.h"
|
|
|
|
#include "../../core/windows/SDL_windows.h"
|
|
#define COBJMACROS
|
|
#include "windows.gaming.input.h"
|
|
#include <cfgmgr32.h>
|
|
#include <objidlbase.h>
|
|
#include <roapi.h>
|
|
#include <initguid.h>
|
|
|
|
#ifdef ____FIReference_1_INT32_INTERFACE_DEFINED__
|
|
/* MinGW-64 uses __FIReference_1_INT32 instead of Microsoft's __FIReference_1_int */
|
|
#define __FIReference_1_int __FIReference_1_INT32
|
|
#define __FIReference_1_int_get_Value __FIReference_1_INT32_get_Value
|
|
#define __FIReference_1_int_Release __FIReference_1_INT32_Release
|
|
#endif
|
|
|
|
struct joystick_hwdata
|
|
{
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller;
|
|
__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller;
|
|
__x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo *battery;
|
|
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
|
|
__x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
|
|
UINT64 timestamp;
|
|
};
|
|
|
|
typedef struct WindowsGamingInputControllerState
|
|
{
|
|
SDL_JoystickID instance_id;
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller;
|
|
char *name;
|
|
SDL_JoystickGUID guid;
|
|
SDL_JoystickType type;
|
|
} WindowsGamingInputControllerState;
|
|
|
|
typedef HRESULT(WINAPI *CoIncrementMTAUsage_t)(PVOID *pCookie);
|
|
typedef HRESULT(WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void **factory);
|
|
typedef HRESULT(WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING *string);
|
|
typedef HRESULT(WINAPI *WindowsDeleteString_t)(HSTRING string);
|
|
typedef PCWSTR(WINAPI *WindowsGetStringRawBuffer_t)(HSTRING string, UINT32 *length);
|
|
|
|
static struct
|
|
{
|
|
CoIncrementMTAUsage_t CoIncrementMTAUsage;
|
|
RoGetActivationFactory_t RoGetActivationFactory;
|
|
WindowsCreateStringReference_t WindowsCreateStringReference;
|
|
WindowsDeleteString_t WindowsDeleteString;
|
|
WindowsGetStringRawBuffer_t WindowsGetStringRawBuffer;
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics *controller_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;
|
|
|
|
/* WinRT headers in official Windows SDK contain only declarations, and we have to define these GUIDs ourselves.
|
|
* https://stackoverflow.com/a/55605485/1795050
|
|
*/
|
|
DEFINE_GUID(IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController, 0x00621c22, 0x42e8, 0x529f, 0x92, 0x70, 0x83, 0x6b, 0x32, 0x93, 0x1d, 0x72);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, 0x5c37b8c8, 0x37b1, 0x4ad8, 0x94, 0x58, 0x20, 0x0f, 0x1a, 0x30, 0x01, 0x8e);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, 0x52b5d744, 0xbb86, 0x445a, 0xb5, 0x9c, 0x59, 0x6f, 0x0e, 0x2a, 0x49, 0xdf);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, 0x5514924a, 0xfecc, 0x435e, 0x83, 0xdc, 0x5c, 0xec, 0x8a, 0x18, 0xa5, 0x20);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameController, 0x1baf6522, 0x5f64, 0x42c5, 0x82, 0x67, 0xb9, 0xfe, 0x22, 0x15, 0xbf, 0xbd);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, 0xdcecc681, 0x3963, 0x4da6, 0x95, 0x5d, 0x55, 0x3f, 0x3b, 0x6f, 0x61, 0x61);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, 0x8bbce529, 0xd49c, 0x39e9, 0x95, 0x60, 0xe4, 0x7d, 0xde, 0x96, 0xb7, 0xc8);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, 0x42676dc5, 0x0856, 0x47c4, 0x92, 0x13, 0xb3, 0x95, 0x50, 0x4c, 0x3a, 0x3c);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, 0x3ac12cd5, 0x581b, 0x4936, 0x9f, 0x94, 0x69, 0xf1, 0xe6, 0x51, 0x4c, 0x7d);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, 0xe666bcaa, 0xedfd, 0x4323, 0xa9, 0xf6, 0x3c, 0x38, 0x40, 0x48, 0xd1, 0xed);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, 0x7cad6d91, 0xa7e1, 0x4f71, 0x9a, 0x78, 0x33, 0xe9, 0xc5, 0xdf, 0xea, 0x62);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035, 0xbb73, 0x4756, 0xa7, 0x87, 0x3e, 0xd6, 0xbe, 0xa6, 0x17, 0xbd);
|
|
DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e);
|
|
|
|
extern SDL_bool SDL_XINPUT_Enabled(void);
|
|
extern SDL_bool SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version);
|
|
|
|
|
|
static SDL_bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product)
|
|
{
|
|
#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT)
|
|
PRAWINPUTDEVICELIST raw_devices = NULL;
|
|
UINT i, raw_device_count = 0;
|
|
LONG vidpid = MAKELONG(vendor, product);
|
|
|
|
/* XInput and RawInput backends will pick up XInput-compatible devices */
|
|
if (!SDL_XINPUT_Enabled()
|
|
#ifdef SDL_JOYSTICK_RAWINPUT
|
|
&& !RAWINPUT_IsEnabled()
|
|
#endif
|
|
) {
|
|
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) {
|
|
return SDL_FALSE;
|
|
}
|
|
|
|
raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST));
|
|
if (raw_device_count == (UINT)-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[MAX_PATH] = { 0 };
|
|
UINT rdiSize = sizeof(rdi);
|
|
UINT nameSize = SDL_arraysize(devName);
|
|
DEVINST devNode;
|
|
char devVidPidString[32];
|
|
int j;
|
|
|
|
rdi.cbSize = sizeof(rdi);
|
|
|
|
if ((raw_devices[i].dwType != RIM_TYPEHID) ||
|
|
(GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) ||
|
|
(GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) ||
|
|
(SDL_strstr(devName, "IG_") == NULL)) {
|
|
/* Skip non-XInput devices */
|
|
continue;
|
|
}
|
|
|
|
/* First check for a simple VID/PID match. This will work for Xbox 360 controllers. */
|
|
if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) {
|
|
SDL_free(raw_devices);
|
|
return SDL_TRUE;
|
|
}
|
|
|
|
/* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack.
|
|
* We'll have to walk the device tree upwards searching for a match for our VID/PID. */
|
|
|
|
/* Make sure the device interface string is something we know how to parse */
|
|
/* Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */
|
|
if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) {
|
|
continue;
|
|
}
|
|
|
|
/* Unescape the backslashes in the string and terminate before the GUID portion */
|
|
for (j = 0; devName[j] != '\0'; j++) {
|
|
if (devName[j] == '#') {
|
|
if (devName[j + 1] == '{') {
|
|
devName[j] = '\0';
|
|
break;
|
|
} else {
|
|
devName[j] = '\\';
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000
|
|
* Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */
|
|
if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
(void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product);
|
|
|
|
while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) {
|
|
char deviceId[MAX_DEVICE_ID_LEN];
|
|
|
|
if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) &&
|
|
(SDL_strstr(deviceId, devVidPidString) != NULL)) {
|
|
/* The VID/PID matched a parent device */
|
|
SDL_free(raw_devices);
|
|
return SDL_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_free(raw_devices);
|
|
#endif /* SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT */
|
|
|
|
return SDL_FALSE;
|
|
}
|
|
|
|
static void WGI_LoadRawGameControllerStatics()
|
|
{
|
|
HRESULT hr;
|
|
HSTRING_HEADER class_name_header;
|
|
HSTRING class_name;
|
|
|
|
hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RawGameController, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RawGameController), &class_name_header, &class_name);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, (void **)&wgi.controller_statics);
|
|
if (!SUCCEEDED(hr)) {
|
|
WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRawGameControllerStatics", hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void WGI_LoadOtherControllerStatics()
|
|
{
|
|
HRESULT hr;
|
|
HSTRING_HEADER class_name_header;
|
|
HSTRING class_name;
|
|
|
|
if (!wgi.arcade_stick_statics) {
|
|
hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_ArcadeStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_ArcadeStick), &class_name_header, &class_name);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics, (void **)&wgi.arcade_stick_statics);
|
|
if (SUCCEEDED(hr)) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics_QueryInterface(wgi.arcade_stick_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2, (void **)&wgi.arcade_stick_statics2);
|
|
} else {
|
|
WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IArcadeStickStatics", hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!wgi.flight_stick_statics) {
|
|
hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_FlightStick, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_FlightStick), &class_name_header, &class_name);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics, (void **)&wgi.flight_stick_statics);
|
|
if (!SUCCEEDED(hr)) {
|
|
WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IFlightStickStatics", hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!wgi.gamepad_statics) {
|
|
hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_Gamepad, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_Gamepad), &class_name_header, &class_name);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics, (void **)&wgi.gamepad_statics);
|
|
if (SUCCEEDED(hr)) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_QueryInterface(wgi.gamepad_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2, (void **)&wgi.gamepad_statics2);
|
|
} else {
|
|
WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IGamepadStatics", hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!wgi.racing_wheel_statics) {
|
|
hr = wgi.WindowsCreateStringReference(RuntimeClass_Windows_Gaming_Input_RacingWheel, (UINT32)SDL_wcslen(RuntimeClass_Windows_Gaming_Input_RacingWheel), &class_name_header, &class_name);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = wgi.RoGetActivationFactory(class_name, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics, (void **)&wgi.racing_wheel_statics);
|
|
if (SUCCEEDED(hr)) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics_QueryInterface(wgi.racing_wheel_statics, &IID___x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2, (void **)&wgi.racing_wheel_statics2);
|
|
} else {
|
|
WIN_SetErrorFromHRESULT("Couldn't find Windows.Gaming.Input.IRacingWheelStatics", hr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static SDL_JoystickType GetGameControllerType(__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller)
|
|
{
|
|
__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;
|
|
|
|
/* Wait to initialize these interfaces until we need them.
|
|
* Initializing the gamepad interface will switch Bluetooth PS4 controllers into enhanced mode, breaking DirectInput
|
|
*/
|
|
WGI_LoadOtherControllerStatics();
|
|
|
|
if (wgi.gamepad_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, game_controller, &gamepad)) && gamepad) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
|
|
return SDL_JOYSTICK_TYPE_GAMEPAD;
|
|
}
|
|
|
|
if (wgi.arcade_stick_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIArcadeStickStatics2_FromGameController(wgi.arcade_stick_statics2, game_controller, &arcade_stick)) && arcade_stick) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIArcadeStick_Release(arcade_stick);
|
|
return SDL_JOYSTICK_TYPE_ARCADE_STICK;
|
|
}
|
|
|
|
if (wgi.flight_stick_statics && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIFlightStickStatics_FromGameController(wgi.flight_stick_statics, game_controller, &flight_stick)) && flight_stick) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIFlightStick_Release(flight_stick);
|
|
return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
|
|
}
|
|
|
|
if (wgi.racing_wheel_statics2 && SUCCEEDED(__x_ABI_CWindows_CGaming_CInput_CIRacingWheelStatics2_FromGameController(wgi.racing_wheel_statics2, game_controller, &racing_wheel)) && racing_wheel) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIRacingWheel_Release(racing_wheel);
|
|
return SDL_JOYSTICK_TYPE_WHEEL;
|
|
}
|
|
|
|
return SDL_JOYSTICK_TYPE_UNKNOWN;
|
|
}
|
|
|
|
typedef struct RawGameControllerDelegate
|
|
{
|
|
__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController iface;
|
|
SDL_AtomicInt refcount;
|
|
} RawGameControllerDelegate;
|
|
|
|
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_IAgileObject) || WIN_IsEqualIID(riid, &IID___FIEventHandler_1_Windows__CGaming__CInput__CRawGameController)) {
|
|
*ppvObject = This;
|
|
__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_AddRef(This);
|
|
return S_OK;
|
|
} else if (WIN_IsEqualIID(riid, &IID_IMarshal)) {
|
|
/* This seems complicated. Let's hope it doesn't happen. */
|
|
return E_OUTOFMEMORY;
|
|
} else {
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_AddRef(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
|
|
{
|
|
RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
|
|
return SDL_AtomicAdd(&self->refcount, 1) + 1UL;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_Release(__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController *This)
|
|
{
|
|
RawGameControllerDelegate *self = (RawGameControllerDelegate *)This;
|
|
int rc = SDL_AtomicAdd(&self->refcount, -1) - 1;
|
|
/* Should never free the static delegate objects */
|
|
SDL_assert(rc > 0);
|
|
return rc;
|
|
}
|
|
|
|
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;
|
|
|
|
SDL_LockJoysticks();
|
|
|
|
/* We can get delayed calls to InvokeAdded() after WGI_JoystickQuit() */
|
|
if (SDL_JoysticksQuitting() || !SDL_JoysticksInitialized()) {
|
|
SDL_UnlockJoysticks();
|
|
return S_OK;
|
|
}
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (void **)&controller);
|
|
if (SUCCEEDED(hr)) {
|
|
char *name = NULL;
|
|
SDL_JoystickGUID guid = { 0 };
|
|
Uint16 bus = SDL_HARDWARE_BUS_USB;
|
|
Uint16 vendor = 0;
|
|
Uint16 product = 0;
|
|
Uint16 version = 0;
|
|
SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN;
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController2 *controller2 = NULL;
|
|
__x_ABI_CWindows_CGaming_CInput_CIGameController *game_controller = NULL;
|
|
SDL_bool ignore_joystick = SDL_FALSE;
|
|
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareVendorId(controller, &vendor);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_HardwareProductId(controller, &product);
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, (void **)&controller2);
|
|
if (SUCCEEDED(hr)) {
|
|
HSTRING hString;
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_get_DisplayName(controller2, &hString);
|
|
if (SUCCEEDED(hr)) {
|
|
PCWSTR string = wgi.WindowsGetStringRawBuffer(hString, NULL);
|
|
if (string) {
|
|
name = WIN_StringToUTF8W(string);
|
|
}
|
|
wgi.WindowsDeleteString(hString);
|
|
}
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);
|
|
}
|
|
if (!name) {
|
|
name = SDL_strdup("");
|
|
}
|
|
|
|
#ifdef SDL_JOYSTICK_HIDAPI
|
|
if (!ignore_joystick && HIDAPI_IsDevicePresent(vendor, product, version, name)) {
|
|
ignore_joystick = SDL_TRUE;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SDL_JOYSTICK_RAWINPUT
|
|
if (!ignore_joystick && RAWINPUT_IsDevicePresent(vendor, product, version, name)) {
|
|
ignore_joystick = SDL_TRUE;
|
|
}
|
|
#endif
|
|
|
|
if (!ignore_joystick && SDL_DINPUT_JoystickPresent(vendor, product, version)) {
|
|
ignore_joystick = SDL_TRUE;
|
|
}
|
|
|
|
if (!ignore_joystick && SDL_IsXInputDevice(vendor, product)) {
|
|
ignore_joystick = SDL_TRUE;
|
|
}
|
|
|
|
if (!ignore_joystick) {
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&game_controller);
|
|
if (SUCCEEDED(hr)) {
|
|
boolean wireless;
|
|
|
|
type = GetGameControllerType(game_controller);
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(game_controller, &wireless);
|
|
if (SUCCEEDED(hr) && wireless) {
|
|
bus = SDL_HARDWARE_BUS_BLUETOOTH;
|
|
}
|
|
|
|
__x_ABI_CWindows_CGaming_CInput_CIGameController_Release(game_controller);
|
|
}
|
|
|
|
guid = SDL_CreateJoystickGUID(bus, vendor, product, version, name, 'w', (Uint8)type);
|
|
|
|
if (SDL_ShouldIgnoreJoystick(name, guid)) {
|
|
ignore_joystick = SDL_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!ignore_joystick) {
|
|
/* 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_GetNextObjectID();
|
|
|
|
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_AddRef(controller);
|
|
|
|
++wgi.controller_count;
|
|
wgi.controllers = controllers;
|
|
|
|
SDL_PrivateJoystickAdded(joystickID);
|
|
} else {
|
|
SDL_free(name);
|
|
}
|
|
} else {
|
|
SDL_free(name);
|
|
}
|
|
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
|
|
}
|
|
|
|
SDL_UnlockJoysticks();
|
|
|
|
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;
|
|
|
|
SDL_LockJoysticks();
|
|
|
|
/* Can we get delayed calls to InvokeRemoved() after WGI_JoystickQuit()? */
|
|
if (!SDL_JoysticksInitialized()) {
|
|
SDL_UnlockJoysticks();
|
|
return S_OK;
|
|
}
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(e, &IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController, (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);
|
|
}
|
|
|
|
SDL_UnlockJoysticks();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4028) /* formal parameter 3 different from declaration, when using older buggy WGI headers */
|
|
#endif
|
|
|
|
static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_added_vtbl = {
|
|
IEventHandler_CRawGameControllerVtbl_QueryInterface,
|
|
IEventHandler_CRawGameControllerVtbl_AddRef,
|
|
IEventHandler_CRawGameControllerVtbl_Release,
|
|
IEventHandler_CRawGameControllerVtbl_InvokeAdded
|
|
};
|
|
static RawGameControllerDelegate controller_added = {
|
|
{ &controller_added_vtbl },
|
|
{ 1 }
|
|
};
|
|
|
|
static __FIEventHandler_1_Windows__CGaming__CInput__CRawGameControllerVtbl controller_removed_vtbl = {
|
|
IEventHandler_CRawGameControllerVtbl_QueryInterface,
|
|
IEventHandler_CRawGameControllerVtbl_AddRef,
|
|
IEventHandler_CRawGameControllerVtbl_Release,
|
|
IEventHandler_CRawGameControllerVtbl_InvokeRemoved
|
|
};
|
|
static RawGameControllerDelegate controller_removed = {
|
|
{ &controller_removed_vtbl },
|
|
{ 1 }
|
|
};
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
static int WGI_JoystickInit(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_WGI, SDL_TRUE)) {
|
|
return 0;
|
|
}
|
|
|
|
if (FAILED(WIN_RoInitialize())) {
|
|
return SDL_SetError("RoInitialize() failed");
|
|
}
|
|
|
|
#ifdef __WINRT__
|
|
wgi.CoIncrementMTAUsage = CoIncrementMTAUsage;
|
|
wgi.RoGetActivationFactory = RoGetActivationFactory;
|
|
wgi.WindowsCreateStringReference = WindowsCreateStringReference;
|
|
wgi.WindowsDeleteString = WindowsDeleteString;
|
|
wgi.WindowsGetStringRawBuffer = WindowsGetStringRawBuffer;
|
|
#else
|
|
#define RESOLVE(x) wgi.x = (x##_t)WIN_LoadComBaseFunction(#x); if (!wgi.x) return WIN_SetError("GetProcAddress failed for " #x);
|
|
RESOLVE(CoIncrementMTAUsage);
|
|
RESOLVE(RoGetActivationFactory);
|
|
RESOLVE(WindowsCreateStringReference);
|
|
RESOLVE(WindowsDeleteString);
|
|
RESOLVE(WindowsGetStringRawBuffer);
|
|
#undef RESOLVE
|
|
#endif /* __WINRT__ */
|
|
|
|
#ifndef __WINRT__
|
|
{
|
|
/* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself.
|
|
* This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing.
|
|
* As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later.
|
|
* See https://github.com/libsdl-org/SDL/issues/5552 for more details.
|
|
*/
|
|
static PVOID cookie = NULL;
|
|
if (!cookie) {
|
|
hr = wgi.CoIncrementMTAUsage(&cookie);
|
|
if (FAILED(hr)) {
|
|
return WIN_SetErrorFromHRESULT("CoIncrementMTAUsage() failed", hr);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
WGI_LoadRawGameControllerStatics();
|
|
|
|
if (wgi.controller_statics) {
|
|
__FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers;
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerAdded(wgi.controller_statics, &controller_added.iface, &wgi.controller_added_token);
|
|
if (!SUCCEEDED(hr)) {
|
|
WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerAdded failed", hr);
|
|
}
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_add_RawGameControllerRemoved(wgi.controller_statics, &controller_removed.iface, &wgi.controller_removed_token);
|
|
if (!SUCCEEDED(hr)) {
|
|
WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IRawGameControllerStatics.add_RawGameControllerRemoved failed", hr);
|
|
}
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_get_RawGameControllers(wgi.controller_statics, &controllers);
|
|
if (SUCCEEDED(hr)) {
|
|
unsigned i, count = 0;
|
|
|
|
hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_get_Size(controllers, &count);
|
|
if (SUCCEEDED(hr)) {
|
|
for (i = 0; i < count; ++i) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController *controller = NULL;
|
|
|
|
hr = __FIVectorView_1_Windows__CGaming__CInput__CRawGameController_GetAt(controllers, i, &controller);
|
|
if (SUCCEEDED(hr) && controller) {
|
|
__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_Invoke(&controller_added.iface, NULL, controller);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_Release(controller);
|
|
}
|
|
}
|
|
}
|
|
|
|
__FIVectorView_1_Windows__CGaming__CInput__CRawGameController_Release(controllers);
|
|
}
|
|
}
|
|
|
|
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 const char *WGI_JoystickGetDevicePath(int device_index)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
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 -1;
|
|
}
|
|
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___x_ABI_CWindows_CGaming_CInput_CIGameController, (void **)&hwdata->game_controller);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_QueryInterface(hwdata->controller, &IID___x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo, (void **)&hwdata->battery);
|
|
|
|
if (wgi.gamepad_statics2) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2_FromGameController(wgi.gamepad_statics2, hwdata->game_controller, &hwdata->gamepad);
|
|
}
|
|
|
|
if (hwdata->game_controller) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIGameController_get_IsWireless(hwdata->game_controller, &wireless);
|
|
}
|
|
|
|
/* Initialize the joystick capabilities */
|
|
joystick->epowerlevel = wireless ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_ButtonCount(hwdata->controller, &joystick->nbuttons);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_AxisCount(hwdata->controller, &joystick->naxes);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameController_get_SwitchCount(hwdata->controller, &joystick->nhats);
|
|
|
|
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;
|
|
|
|
/* Note: reusing partially filled vibration data struct */
|
|
hwdata->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
|
|
hwdata->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);
|
|
if (SUCCEEDED(hr)) {
|
|
return 0;
|
|
} else {
|
|
return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);
|
|
}
|
|
} else {
|
|
return SDL_Unsupported();
|
|
}
|
|
}
|
|
|
|
static int WGI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
|
{
|
|
struct joystick_hwdata *hwdata = joystick->hwdata;
|
|
|
|
if (hwdata->gamepad) {
|
|
HRESULT hr;
|
|
|
|
/* Note: reusing partially filled vibration data struct */
|
|
hwdata->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;
|
|
hwdata->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(hwdata->gamepad, hwdata->vibration);
|
|
if (SUCCEEDED(hr)) {
|
|
return 0;
|
|
} else {
|
|
return WIN_SetErrorFromHRESULT("Windows.Gaming.Input.IGamepad.put_Vibration failed", hr);
|
|
}
|
|
} else {
|
|
return SDL_Unsupported();
|
|
}
|
|
}
|
|
|
|
static Uint32 WGI_JoystickGetCapabilities(SDL_Joystick *joystick)
|
|
{
|
|
struct joystick_hwdata *hwdata = joystick->hwdata;
|
|
|
|
if (hwdata->gamepad) {
|
|
/* FIXME: Can WGI even tell us if trigger rumble is supported? */
|
|
return SDL_JOYCAP_RUMBLE | SDL_JOYCAP_RUMBLE_TRIGGERS;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int WGI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
|
{
|
|
return SDL_Unsupported();
|
|
}
|
|
|
|
static int WGI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
|
|
{
|
|
return SDL_Unsupported();
|
|
}
|
|
|
|
static int WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
|
|
{
|
|
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 = SDL_min(joystick->nbuttons, SDL_MAX_UINT8);
|
|
boolean *buttons = NULL;
|
|
UINT32 nhats = SDL_min(joystick->nhats, SDL_MAX_UINT8);
|
|
__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition *hats = NULL;
|
|
UINT32 naxes = SDL_min(joystick->naxes, SDL_MAX_UINT8);
|
|
DOUBLE *axes = NULL;
|
|
UINT64 timestamp;
|
|
|
|
if (nbuttons > 0) {
|
|
buttons = SDL_stack_alloc(boolean, nbuttons);
|
|
}
|
|
if (nhats > 0) {
|
|
hats = SDL_stack_alloc(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition, nhats);
|
|
}
|
|
if (naxes > 0) {
|
|
axes = SDL_stack_alloc(DOUBLE, naxes);
|
|
}
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIRawGameController_GetCurrentReading(hwdata->controller, nbuttons, buttons, nhats, hats, naxes, axes, ×tamp);
|
|
if (SUCCEEDED(hr) && (!timestamp || timestamp != hwdata->timestamp)) {
|
|
UINT32 i;
|
|
SDL_bool all_zero = SDL_FALSE;
|
|
|
|
hwdata->timestamp = timestamp;
|
|
|
|
/* The axes are all zero when the application loses focus */
|
|
if (naxes > 0) {
|
|
all_zero = SDL_TRUE;
|
|
for (i = 0; i < naxes; ++i) {
|
|
if (axes[i] != 0.0f) {
|
|
all_zero = SDL_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (all_zero) {
|
|
SDL_PrivateJoystickForceRecentering(joystick);
|
|
} else {
|
|
/* FIXME: What units are the timestamp we get from GetCurrentReading()? */
|
|
timestamp = SDL_GetTicksNS();
|
|
for (i = 0; i < nbuttons; ++i) {
|
|
SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, buttons[i]);
|
|
}
|
|
for (i = 0; i < nhats; ++i) {
|
|
SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, ConvertHatValue(hats[i]));
|
|
}
|
|
for (i = 0; i < naxes; ++i) {
|
|
SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, (Sint16)((int)(axes[i] * 65535) - 32768));
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_stack_free(buttons);
|
|
SDL_stack_free(hats);
|
|
SDL_stack_free(axes);
|
|
|
|
if (joystick->epowerlevel != SDL_JOYSTICK_POWER_WIRED && hwdata->battery) {
|
|
__x_ABI_CWindows_CDevices_CPower_CIBatteryReport *report = NULL;
|
|
|
|
hr = __x_ABI_CWindows_CGaming_CInput_CIGameControllerBatteryInfo_TryGetBatteryReport(hwdata->battery, &report);
|
|
if (SUCCEEDED(hr) && report) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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->game_controller) {
|
|
__x_ABI_CWindows_CGaming_CInput_CIGameController_Release(hwdata->game_controller);
|
|
}
|
|
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.controller_statics) {
|
|
while (wgi.controller_count > 0) {
|
|
__FIEventHandler_1_Windows__CGaming__CInput__CRawGameController_Invoke(&controller_removed.iface, NULL, 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.controller_statics, wgi.controller_added_token);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.controller_statics, wgi.controller_removed_token);
|
|
__x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.controller_statics);
|
|
}
|
|
|
|
WIN_RoUninitialize();
|
|
|
|
SDL_zero(wgi);
|
|
}
|
|
|
|
static SDL_bool WGI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
|
|
{
|
|
return SDL_FALSE;
|
|
}
|
|
|
|
SDL_JoystickDriver SDL_WGI_JoystickDriver = {
|
|
WGI_JoystickInit,
|
|
WGI_JoystickGetCount,
|
|
WGI_JoystickDetect,
|
|
WGI_JoystickGetDeviceName,
|
|
WGI_JoystickGetDevicePath,
|
|
WGI_JoystickGetDevicePlayerIndex,
|
|
WGI_JoystickSetDevicePlayerIndex,
|
|
WGI_JoystickGetDeviceGUID,
|
|
WGI_JoystickGetDeviceInstanceID,
|
|
WGI_JoystickOpen,
|
|
WGI_JoystickRumble,
|
|
WGI_JoystickRumbleTriggers,
|
|
WGI_JoystickGetCapabilities,
|
|
WGI_JoystickSetLED,
|
|
WGI_JoystickSendEffect,
|
|
WGI_JoystickSetSensorsEnabled,
|
|
WGI_JoystickUpdate,
|
|
WGI_JoystickClose,
|
|
WGI_JoystickQuit,
|
|
WGI_JoystickGetGamepadMapping
|
|
};
|
|
|
|
#endif /* SDL_JOYSTICK_WGI */
|