Path: blob/master/thirdparty/sdl/joystick/windows/SDL_dinputjoystick.c
9905 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#include "../SDL_sysjoystick.h"2324#ifdef SDL_JOYSTICK_DINPUT2526#include "SDL_windowsjoystick_c.h"27#include "SDL_dinputjoystick_c.h"28#include "SDL_rawinputjoystick_c.h"29#include "SDL_xinputjoystick_c.h"30#include "../hidapi/SDL_hidapijoystick_c.h"3132#ifndef DIDFT_OPTIONAL33#define DIDFT_OPTIONAL 0x8000000034#endif3536#define INPUT_QSIZE 128 // Buffer up to 128 input messages37#define JOY_AXIS_THRESHOLD (((SDL_JOYSTICK_AXIS_MAX) - (SDL_JOYSTICK_AXIS_MIN)) / 100) // 1% motion3839#define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF)4041// external variables referenced.42extern HWND SDL_HelperWindow;434445// local variables46static bool coinitialized = false;47static LPDIRECTINPUT8 dinput = NULL;4849// Taken from Wine - Thanks!50static DIOBJECTDATAFORMAT dfDIJoystick2[] = {51{ &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },52{ &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },53{ &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },54{ &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },55{ &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },56{ &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },57{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },58{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },59{ &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },60{ &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },61{ &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },62{ &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },63{ NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },64{ NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },65{ NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },66{ NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },67{ NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },68{ NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },69{ NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },70{ NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },71{ NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },72{ NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },73{ NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },74{ NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },75{ NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },76{ NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },77{ NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },78{ NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },79{ NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },80{ NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },81{ NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },82{ NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },83{ NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },84{ NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },85{ NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },86{ NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },87{ NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },88{ NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },89{ NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },90{ NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },91{ NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },92{ NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },93{ NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },94{ NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },95{ NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },96{ NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },97{ NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },98{ NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },99{ NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },100{ NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },101{ NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },102{ NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },103{ NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },104{ NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },105{ NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },106{ NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },107{ NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },108{ NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },109{ NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },110{ NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },111{ NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },112{ NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },113{ NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },114{ NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },115{ NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },116{ NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },117{ NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },118{ NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },119{ NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },120{ NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },121{ NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },122{ NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },123{ NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },124{ NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },125{ NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },126{ NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },127{ NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },128{ NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },129{ NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },130{ NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },131{ NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },132{ NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },133{ NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },134{ NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },135{ NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },136{ NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },137{ NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },138{ NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },139{ NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },140{ NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },141{ NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },142{ NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },143{ NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },144{ NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },145{ NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },146{ NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },147{ NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },148{ NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },149{ NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },150{ NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },151{ NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },152{ NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },153{ NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },154{ NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },155{ NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },156{ NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },157{ NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },158{ NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },159{ NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },160{ NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },161{ NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },162{ NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },163{ NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },164{ NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },165{ NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },166{ NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },167{ NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },168{ NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },169{ NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },170{ NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },171{ NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },172{ NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },173{ NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },174{ NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },175{ NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },176{ NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },177{ NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },178{ NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },179{ NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },180{ NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },181{ NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },182{ NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },183{ NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },184{ NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },185{ NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },186{ NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },187{ NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },188{ NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },189{ NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },190{ NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },191{ &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },192{ &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },193{ &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },194{ &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },195{ &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },196{ &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },197// note: dwOfs value matches Windows198{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },199{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },200{ &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },201{ &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },202{ &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },203{ &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },204{ &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },205{ &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },206// note: dwOfs value matches Windows207{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },208{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },209{ &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },210{ &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },211{ &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },212{ &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },213{ &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },214{ &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },215// note: dwOfs value matches Windows216{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },217{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },218};219220const DIDATAFORMAT SDL_c_dfDIJoystick2 = {221sizeof(DIDATAFORMAT),222sizeof(DIOBJECTDATAFORMAT),223DIDF_ABSAXIS,224sizeof(DIJOYSTATE2),225SDL_arraysize(dfDIJoystick2),226dfDIJoystick2227};228229// Convert a DirectInput return code to a text message230static bool SetDIerror(const char *function, HRESULT code)231{232return SDL_SetError("%s() DirectX error 0x%8.8lx", function, code);233}234235static bool SDL_IsXInputDevice(Uint16 vendor_id, Uint16 product_id, const char *hidPath)236{237#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT)238SDL_GamepadType type;239240// XInput and RawInput backends will pick up XInput-compatible devices241if (!SDL_XINPUT_Enabled()242#ifdef SDL_JOYSTICK_RAWINPUT243&& !RAWINPUT_IsEnabled()244#endif245) {246return false;247}248249// If device path contains "IG_" then its an XInput device250// See: https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput251if (SDL_strstr(hidPath, "IG_") != NULL) {252return true;253}254255type = SDL_GetGamepadTypeFromVIDPID(vendor_id, product_id, NULL, false);256if (type == SDL_GAMEPAD_TYPE_XBOX360 ||257type == SDL_GAMEPAD_TYPE_XBOXONE ||258(vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) {259return true;260}261#endif // SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT262263return false;264}265266static bool QueryDeviceName(LPDIRECTINPUTDEVICE8 device, Uint16 vendor_id, Uint16 product_id, char **manufacturer_string, char **product_string)267{268DIPROPSTRING dipstr;269270if (!device || !manufacturer_string || !product_string) {271return false;272}273274#ifdef SDL_JOYSTICK_HIDAPI275*manufacturer_string = HIDAPI_GetDeviceManufacturerName(vendor_id, product_id);276*product_string = HIDAPI_GetDeviceProductName(vendor_id, product_id);277if (*product_string) {278return true;279}280#endif281282dipstr.diph.dwSize = sizeof(dipstr);283dipstr.diph.dwHeaderSize = sizeof(dipstr.diph);284dipstr.diph.dwObj = 0;285dipstr.diph.dwHow = DIPH_DEVICE;286287if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_PRODUCTNAME, &dipstr.diph))) {288return false;289}290291*manufacturer_string = NULL;292*product_string = WIN_StringToUTF8(dipstr.wsz);293294return true;295}296297static bool QueryDevicePath(LPDIRECTINPUTDEVICE8 device, char **device_path)298{299DIPROPGUIDANDPATH dippath;300301if (!device || !device_path) {302return false;303}304305dippath.diph.dwSize = sizeof(dippath);306dippath.diph.dwHeaderSize = sizeof(dippath.diph);307dippath.diph.dwObj = 0;308dippath.diph.dwHow = DIPH_DEVICE;309310if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_GUIDANDPATH, &dippath.diph))) {311return false;312}313314*device_path = WIN_StringToUTF8W(dippath.wszPath);315316// Normalize path to upper case.317SDL_strupr(*device_path);318319return true;320}321322static bool QueryDeviceInfo(LPDIRECTINPUTDEVICE8 device, Uint16 *vendor_id, Uint16 *product_id)323{324DIPROPDWORD dipdw;325326if (!device || !vendor_id || !product_id) {327return false;328}329330dipdw.diph.dwSize = sizeof(dipdw);331dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);332dipdw.diph.dwObj = 0;333dipdw.diph.dwHow = DIPH_DEVICE;334dipdw.dwData = 0;335336if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_VIDPID, &dipdw.diph))) {337return false;338}339340*vendor_id = LOWORD(dipdw.dwData);341*product_id = HIWORD(dipdw.dwData);342343return true;344}345346void FreeRumbleEffectData(DIEFFECT *effect)347{348if (!effect) {349return;350}351SDL_free(effect->rgdwAxes);352SDL_free(effect->rglDirection);353SDL_free(effect->lpvTypeSpecificParams);354SDL_free(effect);355}356357DIEFFECT *CreateRumbleEffectData(Sint16 magnitude)358{359DIEFFECT *effect;360DIPERIODIC *periodic;361362// Create the effect363effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));364if (!effect) {365return NULL;366}367effect->dwSize = sizeof(*effect);368effect->dwGain = 10000;369effect->dwFlags = DIEFF_OBJECTOFFSETS;370effect->dwDuration = SDL_MAX_RUMBLE_DURATION_MS * 1000; // In microseconds.371effect->dwTriggerButton = DIEB_NOTRIGGER;372373effect->cAxes = 2;374effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));375if (!effect->rgdwAxes) {376FreeRumbleEffectData(effect);377return NULL;378}379380effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));381if (!effect->rglDirection) {382FreeRumbleEffectData(effect);383return NULL;384}385effect->dwFlags |= DIEFF_CARTESIAN;386387periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));388if (!periodic) {389FreeRumbleEffectData(effect);390return NULL;391}392periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);393periodic->dwPeriod = 1000000;394395effect->cbTypeSpecificParams = sizeof(*periodic);396effect->lpvTypeSpecificParams = periodic;397398return effect;399}400401bool SDL_DINPUT_JoystickInit(void)402{403HRESULT result;404HINSTANCE instance;405406if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_DIRECTINPUT, true)) {407// In some environments, IDirectInput8_Initialize / _EnumDevices can take a minute even with no controllers.408dinput = NULL;409return true;410}411412result = WIN_CoInitialize();413if (FAILED(result)) {414return SetDIerror("CoInitialize", result);415}416417coinitialized = true;418419result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,420&IID_IDirectInput8, (LPVOID *)&dinput);421422if (FAILED(result)) {423return SetDIerror("CoCreateInstance", result);424}425426// Because we used CoCreateInstance, we need to Initialize it, first.427instance = GetModuleHandle(NULL);428if (!instance) {429IDirectInput8_Release(dinput);430dinput = NULL;431return SDL_SetError("GetModuleHandle() failed with error code %lu.", GetLastError());432}433result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);434435if (FAILED(result)) {436IDirectInput8_Release(dinput);437dinput = NULL;438return SetDIerror("IDirectInput::Initialize", result);439}440return true;441}442443static int GetSteamVirtualGamepadSlot(Uint16 vendor_id, Uint16 product_id, const char *device_path)444{445int slot = -1;446447if (vendor_id == USB_VENDOR_VALVE &&448product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {449(void)SDL_sscanf(device_path, "\\\\?\\HID#VID_28DE&PID_11FF&IG_0%d", &slot);450}451return slot;452}453454// helper function for direct input, gets called for each connected joystick455static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)456{457#define CHECK(expression) \458{ \459if (!(expression)) \460goto err; \461}462JoyStick_DeviceData *pNewJoystick = NULL;463JoyStick_DeviceData *pPrevJoystick = NULL;464Uint16 vendor = 0;465Uint16 product = 0;466Uint16 version = 0;467char *hidPath = NULL;468char *manufacturer_string = NULL;469char *product_string = NULL;470LPDIRECTINPUTDEVICE8 device = NULL;471472// We are only supporting HID devices.473CHECK(pDeviceInstance->dwDevType & DIDEVTYPE_HID);474475CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL)));476CHECK(QueryDevicePath(device, &hidPath));477CHECK(QueryDeviceInfo(device, &vendor, &product));478CHECK(QueryDeviceName(device, vendor, product, &manufacturer_string, &product_string));479480CHECK(!SDL_IsXInputDevice(vendor, product, hidPath));481CHECK(!SDL_ShouldIgnoreJoystick(vendor, product, version, product_string));482CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, product_string));483484pNewJoystick = *(JoyStick_DeviceData **)pContext;485while (pNewJoystick) {486// update GUIDs of joysticks with matching paths, in case they're not open yet487if (SDL_strcmp(pNewJoystick->path, hidPath) == 0) {488// if we are replacing the front of the list then update it489if (pNewJoystick == *(JoyStick_DeviceData **)pContext) {490*(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;491} else if (pPrevJoystick) {492pPrevJoystick->pNext = pNewJoystick->pNext;493}494495// Update with new guid/etc, if it has changed496SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));497498pNewJoystick->pNext = SYS_Joystick;499SYS_Joystick = pNewJoystick;500501pNewJoystick = NULL;502CHECK(FALSE);503}504505pPrevJoystick = pNewJoystick;506pNewJoystick = pNewJoystick->pNext;507}508509pNewJoystick = (JoyStick_DeviceData *)SDL_calloc(1, sizeof(JoyStick_DeviceData));510CHECK(pNewJoystick);511512pNewJoystick->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(vendor, product, hidPath);513SDL_strlcpy(pNewJoystick->path, hidPath, SDL_arraysize(pNewJoystick->path));514SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));515516pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string);517CHECK(pNewJoystick->joystickname);518519if (vendor && product) {520pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, vendor, product, version, manufacturer_string, product_string, 0, 0);521} else {522pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, version, manufacturer_string, product_string, 0, 0);523}524525WINDOWS_AddJoystickDevice(pNewJoystick);526pNewJoystick = NULL;527528err:529if (pNewJoystick) {530SDL_free(pNewJoystick->joystickname);531SDL_free(pNewJoystick);532}533534SDL_free(hidPath);535SDL_free(manufacturer_string);536SDL_free(product_string);537538if (device) {539IDirectInputDevice8_Release(device);540}541542return DIENUM_CONTINUE; // get next device, please543#undef CHECK544}545546void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)547{548if (!dinput) {549return;550}551552IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickDetectCallback, pContext, DIEDFL_ATTACHEDONLY);553}554555// helper function for direct input, gets called for each connected joystick556typedef struct557{558Uint16 vendor;559Uint16 product;560bool present;561} Joystick_PresentData;562563static BOOL CALLBACK EnumJoystickPresentCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)564{565#define CHECK(expression) \566{ \567if (!(expression)) \568goto err; \569}570Joystick_PresentData *pData = (Joystick_PresentData *)pContext;571Uint16 vendor = 0;572Uint16 product = 0;573LPDIRECTINPUTDEVICE8 device = NULL;574BOOL result = DIENUM_CONTINUE;575576// We are only supporting HID devices.577CHECK(pDeviceInstance->dwDevType & DIDEVTYPE_HID);578579CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL)));580CHECK(QueryDeviceInfo(device, &vendor, &product));581582if (vendor == pData->vendor && product == pData->product) {583pData->present = true;584result = DIENUM_STOP; // found it585}586587err:588if (device) {589IDirectInputDevice8_Release(device);590}591592return result;593#undef CHECK594}595596bool SDL_DINPUT_JoystickPresent(Uint16 vendor_id, Uint16 product_id, Uint16 version_number)597{598Joystick_PresentData data;599600if (!dinput) {601return false;602}603604data.vendor = vendor_id;605data.product = product_id;606data.present = false;607IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickPresentCallback, &data, DIEDFL_ATTACHEDONLY);608return data.present;609}610611static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE pDeviceObject, LPVOID pContext)612{613SDL_Joystick *joystick = (SDL_Joystick *)pContext;614HRESULT result;615input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];616617if (pDeviceObject->dwType & DIDFT_BUTTON) {618in->type = BUTTON;619in->num = (Uint8)joystick->nbuttons;620in->ofs = DIJOFS_BUTTON(in->num);621joystick->nbuttons++;622} else if (pDeviceObject->dwType & DIDFT_POV) {623in->type = HAT;624in->num = (Uint8)joystick->nhats;625in->ofs = DIJOFS_POV(in->num);626joystick->nhats++;627} else if (pDeviceObject->dwType & DIDFT_AXIS) {628DIPROPRANGE diprg;629DIPROPDWORD dilong;630631in->type = AXIS;632in->num = (Uint8)joystick->naxes;633if (SDL_memcmp(&pDeviceObject->guidType, &GUID_XAxis, sizeof(pDeviceObject->guidType)) == 0) {634in->ofs = DIJOFS_X;635} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_YAxis, sizeof(pDeviceObject->guidType)) == 0) {636in->ofs = DIJOFS_Y;637} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_ZAxis, sizeof(pDeviceObject->guidType)) == 0) {638in->ofs = DIJOFS_Z;639} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_RxAxis, sizeof(pDeviceObject->guidType)) == 0) {640in->ofs = DIJOFS_RX;641} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_RyAxis, sizeof(pDeviceObject->guidType)) == 0) {642in->ofs = DIJOFS_RY;643} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_RzAxis, sizeof(pDeviceObject->guidType)) == 0) {644in->ofs = DIJOFS_RZ;645} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_Slider, sizeof(pDeviceObject->guidType)) == 0) {646in->ofs = DIJOFS_SLIDER(joystick->hwdata->NumSliders);647++joystick->hwdata->NumSliders;648} else {649return DIENUM_CONTINUE; // not an axis we can grok650}651652diprg.diph.dwSize = sizeof(diprg);653diprg.diph.dwHeaderSize = sizeof(diprg.diph);654diprg.diph.dwObj = pDeviceObject->dwType;655diprg.diph.dwHow = DIPH_BYID;656diprg.lMin = SDL_JOYSTICK_AXIS_MIN;657diprg.lMax = SDL_JOYSTICK_AXIS_MAX;658659result =660IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,661DIPROP_RANGE, &diprg.diph);662if (FAILED(result)) {663return DIENUM_CONTINUE; // don't use this axis664}665666// Set dead zone to 0.667dilong.diph.dwSize = sizeof(dilong);668dilong.diph.dwHeaderSize = sizeof(dilong.diph);669dilong.diph.dwObj = pDeviceObject->dwType;670dilong.diph.dwHow = DIPH_BYID;671dilong.dwData = 0;672result =673IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,674DIPROP_DEADZONE, &dilong.diph);675if (FAILED(result)) {676return DIENUM_CONTINUE; // don't use this axis677}678679joystick->naxes++;680} else {681// not supported at this time682return DIENUM_CONTINUE;683}684685joystick->hwdata->NumInputs++;686687if (joystick->hwdata->NumInputs == MAX_INPUTS) {688return DIENUM_STOP; // too many689}690691return DIENUM_CONTINUE;692}693694/* Sort using the data offset into the DInput struct.695* This gives a reasonable ordering for the inputs.696*/697static int SDLCALL SortDevFunc(const void *a, const void *b)698{699const input_t *inputA = (const input_t *)a;700const input_t *inputB = (const input_t *)b;701702if (inputA->ofs < inputB->ofs) {703return -1;704}705if (inputA->ofs > inputB->ofs) {706return 1;707}708return 0;709}710711// Sort the input objects and recalculate the indices for each input.712static void SortDevObjects(SDL_Joystick *joystick)713{714input_t *inputs = joystick->hwdata->Inputs;715Uint8 nButtons = 0;716Uint8 nHats = 0;717Uint8 nAxis = 0;718int n;719720SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);721722for (n = 0; n < joystick->hwdata->NumInputs; n++) {723switch (inputs[n].type) {724case BUTTON:725inputs[n].num = nButtons;726nButtons++;727break;728729case HAT:730inputs[n].num = nHats;731nHats++;732break;733734case AXIS:735inputs[n].num = nAxis;736nAxis++;737break;738}739}740}741742bool SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)743{744HRESULT result;745DIPROPDWORD dipdw;746747joystick->hwdata->buffered = true;748joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);749750SDL_zero(dipdw);751dipdw.diph.dwSize = sizeof(DIPROPDWORD);752dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);753754result =755IDirectInput8_CreateDevice(dinput,756&joystickdevice->dxdevice.guidInstance,757&joystick->hwdata->InputDevice,758NULL);759if (FAILED(result)) {760return SetDIerror("IDirectInput::CreateDevice", result);761}762763/* Acquire shared access. Exclusive access is required for forces,764* though. */765result =766IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->InputDevice, SDL_HelperWindow,767DISCL_EXCLUSIVE |768DISCL_BACKGROUND);769if (FAILED(result)) {770return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);771}772773// Use the extended data structure: DIJOYSTATE2.774result =775IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,776&SDL_c_dfDIJoystick2);777if (FAILED(result)) {778return SetDIerror("IDirectInputDevice8::SetDataFormat", result);779}780781// Get device capabilities782result =783IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,784&joystick->hwdata->Capabilities);785if (FAILED(result)) {786return SetDIerror("IDirectInputDevice8::GetCapabilities", result);787}788789// Force capable?790if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {791result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);792if (FAILED(result)) {793return SetDIerror("IDirectInputDevice8::Acquire", result);794}795796// reset all actuators.797result =798IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice,799DISFFC_RESET);800801/* Not necessarily supported, ignore if not supported.802if (FAILED(result)) {803return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);804}805*/806807result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);808809if (FAILED(result)) {810return SetDIerror("IDirectInputDevice8::Unacquire", result);811}812813/* Turn on auto-centering for a ForceFeedback device (until told814* otherwise). */815dipdw.diph.dwObj = 0;816dipdw.diph.dwHow = DIPH_DEVICE;817dipdw.dwData = DIPROPAUTOCENTER_ON;818819result =820IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,821DIPROP_AUTOCENTER, &dipdw.diph);822823/* Not necessarily supported, ignore if not supported.824if (FAILED(result)) {825return SetDIerror("IDirectInputDevice8::SetProperty", result);826}827*/828829SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);830}831832// What buttons and axes does it have?833IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,834EnumDevObjectsCallback, joystick,835DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);836837/* Reorder the input objects. Some devices do not report the X axis as838* the first axis, for example. */839SortDevObjects(joystick);840841dipdw.diph.dwObj = 0;842dipdw.diph.dwHow = DIPH_DEVICE;843dipdw.dwData = INPUT_QSIZE;844845// Set the buffer size846result =847IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,848DIPROP_BUFFERSIZE, &dipdw.diph);849850if (result == DI_POLLEDDEVICE) {851/* This device doesn't support buffering, so we're forced852* to use less reliable polling. */853joystick->hwdata->buffered = false;854} else if (FAILED(result)) {855return SetDIerror("IDirectInputDevice8::SetProperty", result);856}857joystick->hwdata->first_update = true;858859// Poll and wait for initial device state to be populated860result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);861if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {862IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);863IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);864}865SDL_Delay(50);866867return true;868}869870static bool SDL_DINPUT_JoystickInitRumble(SDL_Joystick *joystick, Sint16 magnitude)871{872HRESULT result;873874// Reset and then enable actuators875result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);876if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {877result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);878if (SUCCEEDED(result)) {879result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);880}881}882if (FAILED(result)) {883return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);884}885886result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);887if (FAILED(result)) {888return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);889}890891// Create the effect892joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude);893if (!joystick->hwdata->ffeffect) {894return false;895}896897result = IDirectInputDevice8_CreateEffect(joystick->hwdata->InputDevice, &GUID_Sine,898joystick->hwdata->ffeffect, &joystick->hwdata->ffeffect_ref, NULL);899if (FAILED(result)) {900return SetDIerror("IDirectInputDevice8::CreateEffect", result);901}902return true;903}904905bool SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)906{907HRESULT result;908909// Scale and average the two rumble strengths910Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);911912if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {913return SDL_Unsupported();914}915916if (joystick->hwdata->ff_initialized) {917DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);918periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);919920result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));921if (result == DIERR_INPUTLOST) {922result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);923if (SUCCEEDED(result)) {924result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));925}926}927if (FAILED(result)) {928return SetDIerror("IDirectInputDevice8::SetParameters", result);929}930} else {931if (!SDL_DINPUT_JoystickInitRumble(joystick, magnitude)) {932return false;933}934joystick->hwdata->ff_initialized = true;935}936937result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);938if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {939result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);940if (SUCCEEDED(result)) {941result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);942}943}944if (FAILED(result)) {945return SetDIerror("IDirectInputDevice8::Start", result);946}947return true;948}949950static Uint8 TranslatePOV(DWORD value)951{952const Uint8 HAT_VALS[] = {953SDL_HAT_UP,954SDL_HAT_UP | SDL_HAT_RIGHT,955SDL_HAT_RIGHT,956SDL_HAT_DOWN | SDL_HAT_RIGHT,957SDL_HAT_DOWN,958SDL_HAT_DOWN | SDL_HAT_LEFT,959SDL_HAT_LEFT,960SDL_HAT_UP | SDL_HAT_LEFT961};962963if (LOWORD(value) == 0xFFFF) {964return SDL_HAT_CENTERED;965}966967// Round the value up:968value += 4500 / 2;969value %= 36000;970value /= 4500;971972if (value >= 8) {973return SDL_HAT_CENTERED; // shouldn't happen974}975976return HAT_VALS[value];977}978979/* Function to update the state of a joystick - called as a device poll.980* This function shouldn't update the joystick structure directly,981* but instead should call SDL_PrivateJoystick*() to deliver events982* and update joystick device state.983*/984static void UpdateDINPUTJoystickState_Polled(SDL_Joystick *joystick)985{986DIJOYSTATE2 state;987HRESULT result;988int i;989Uint64 timestamp = SDL_GetTicksNS();990991result =992IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,993sizeof(DIJOYSTATE2), &state);994if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {995IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);996result =997IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,998sizeof(DIJOYSTATE2), &state);999}10001001if (result != DI_OK) {1002return;1003}10041005// Set each known axis, button and POV.1006for (i = 0; i < joystick->hwdata->NumInputs; ++i) {1007const input_t *in = &joystick->hwdata->Inputs[i];10081009switch (in->type) {1010case AXIS:1011switch (in->ofs) {1012case DIJOFS_X:1013SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lX);1014break;1015case DIJOFS_Y:1016SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lY);1017break;1018case DIJOFS_Z:1019SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lZ);1020break;1021case DIJOFS_RX:1022SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lRx);1023break;1024case DIJOFS_RY:1025SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lRy);1026break;1027case DIJOFS_RZ:1028SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lRz);1029break;1030case DIJOFS_SLIDER(0):1031SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.rglSlider[0]);1032break;1033case DIJOFS_SLIDER(1):1034SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.rglSlider[1]);1035break;1036}1037break;10381039case BUTTON:1040SDL_SendJoystickButton(timestamp, joystick, in->num,1041(state.rgbButtons[in->ofs - DIJOFS_BUTTON0] != 0));1042break;1043case HAT:1044{1045Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);1046SDL_SendJoystickHat(timestamp, joystick, in->num, pos);1047break;1048}1049}1050}1051}10521053static void UpdateDINPUTJoystickState_Buffered(SDL_Joystick *joystick)1054{1055int i;1056HRESULT result;1057DWORD numevents;1058DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];1059Uint64 timestamp = SDL_GetTicksNS();10601061numevents = INPUT_QSIZE;1062result =1063IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,1064sizeof(DIDEVICEOBJECTDATA), evtbuf,1065&numevents, 0);1066if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {1067IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);1068result =1069IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,1070sizeof(DIDEVICEOBJECTDATA),1071evtbuf, &numevents, 0);1072}10731074// Handle the events or punt1075if (FAILED(result)) {1076return;1077}10781079for (i = 0; i < (int)numevents; ++i) {1080int j;10811082for (j = 0; j < joystick->hwdata->NumInputs; ++j) {1083const input_t *in = &joystick->hwdata->Inputs[j];10841085if (evtbuf[i].dwOfs != in->ofs) {1086continue;1087}10881089switch (in->type) {1090case AXIS:1091SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)evtbuf[i].dwData);1092break;1093case BUTTON:1094SDL_SendJoystickButton(timestamp, joystick, in->num,1095(evtbuf[i].dwData != 0));1096break;1097case HAT:1098{1099Uint8 pos = TranslatePOV(evtbuf[i].dwData);1100SDL_SendJoystickHat(timestamp, joystick, in->num, pos);1101} break;1102}1103}1104}11051106if (result == DI_BUFFEROVERFLOW) {1107/* Our buffer wasn't big enough to hold all the queued events,1108* so poll the device to make sure we have the complete state.1109*/1110UpdateDINPUTJoystickState_Polled(joystick);1111}1112}11131114void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick)1115{1116HRESULT result;11171118result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);1119if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {1120IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);1121IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);1122}11231124if (joystick->hwdata->first_update) {1125// Poll to get the initial state of the joystick1126UpdateDINPUTJoystickState_Polled(joystick);1127joystick->hwdata->first_update = false;1128return;1129}11301131if (joystick->hwdata->buffered ) {1132UpdateDINPUTJoystickState_Buffered(joystick);1133} else {1134UpdateDINPUTJoystickState_Polled(joystick);1135}1136}11371138void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)1139{1140if (joystick->hwdata->ffeffect_ref) {1141IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);1142joystick->hwdata->ffeffect_ref = NULL;1143}1144if (joystick->hwdata->ffeffect) {1145FreeRumbleEffectData(joystick->hwdata->ffeffect);1146joystick->hwdata->ffeffect = NULL;1147}1148IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);1149IDirectInputDevice8_Release(joystick->hwdata->InputDevice);1150joystick->hwdata->ff_initialized = false;1151}11521153void SDL_DINPUT_JoystickQuit(void)1154{1155if (dinput != NULL) {1156IDirectInput8_Release(dinput);1157dinput = NULL;1158}11591160if (coinitialized) {1161WIN_CoUninitialize();1162coinitialized = false;1163}1164}11651166#else // !SDL_JOYSTICK_DINPUT11671168typedef struct JoyStick_DeviceData JoyStick_DeviceData;11691170bool SDL_DINPUT_JoystickInit(void)1171{1172return true;1173}11741175void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)1176{1177}11781179bool SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)1180{1181return false;1182}11831184bool SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)1185{1186return SDL_Unsupported();1187}11881189bool SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1190{1191return SDL_Unsupported();1192}11931194void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick)1195{1196}11971198void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)1199{1200}12011202void SDL_DINPUT_JoystickQuit(void)1203{1204}12051206#endif // SDL_JOYSTICK_DINPUT120712081209