Path: blob/master/thirdparty/sdl/joystick/windows/SDL_dinputjoystick.c
21884 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;4344// local variables45static bool coinitialized = false;46static LPDIRECTINPUT8 dinput = NULL;4748// Taken from Wine - Thanks!49static DIOBJECTDATAFORMAT dfDIJoystick2[] = {50{ &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },51{ &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },52{ &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },53{ &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },54{ &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },55{ &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },56{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },57{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },58{ &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },59{ &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },60{ &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },61{ &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },62{ NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },63{ NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },64{ NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },65{ NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },66{ NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },67{ NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },68{ NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },69{ NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },70{ NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },71{ NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },72{ NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },73{ NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },74{ NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },75{ NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },76{ NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },77{ NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },78{ NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },79{ NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },80{ NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },81{ NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },82{ NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },83{ NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },84{ NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },85{ NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },86{ NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },87{ NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },88{ NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },89{ NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },90{ NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },91{ NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },92{ NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },93{ NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },94{ NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },95{ NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },96{ NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },97{ NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },98{ NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },99{ NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },100{ NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },101{ NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },102{ NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },103{ NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },104{ NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },105{ NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },106{ NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },107{ NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },108{ NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },109{ NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },110{ NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },111{ NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },112{ NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },113{ NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },114{ NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },115{ NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },116{ NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },117{ NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },118{ NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },119{ NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },120{ NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },121{ NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },122{ NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },123{ NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },124{ NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },125{ NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },126{ NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },127{ NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },128{ NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },129{ NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },130{ NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },131{ NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },132{ NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },133{ NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },134{ NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },135{ NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },136{ NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },137{ NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },138{ NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },139{ NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },140{ NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },141{ NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },142{ NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },143{ NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },144{ NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },145{ NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },146{ NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },147{ NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },148{ NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },149{ NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },150{ NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },151{ NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },152{ NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },153{ NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },154{ NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },155{ NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },156{ NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },157{ NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },158{ NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },159{ NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },160{ NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },161{ NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },162{ NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },163{ NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },164{ NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },165{ NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },166{ NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },167{ NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },168{ NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },169{ NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },170{ NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },171{ NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },172{ NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },173{ NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },174{ NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },175{ NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },176{ NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },177{ NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },178{ NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },179{ NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },180{ NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },181{ NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },182{ NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },183{ NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },184{ NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },185{ NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },186{ NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },187{ NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },188{ NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },189{ NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },190{ &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },191{ &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },192{ &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },193{ &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },194{ &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },195{ &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },196// note: dwOfs value matches Windows197{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },198{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },199{ &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },200{ &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },201{ &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },202{ &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },203{ &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },204{ &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },205// note: dwOfs value matches Windows206{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },207{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },208{ &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },209{ &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },210{ &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },211{ &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },212{ &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },213{ &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },214// note: dwOfs value matches Windows215{ &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },216{ &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },217};218219const DIDATAFORMAT SDL_c_dfDIJoystick2 = {220sizeof(DIDATAFORMAT),221sizeof(DIOBJECTDATAFORMAT),222DIDF_ABSAXIS,223sizeof(DIJOYSTATE2),224SDL_arraysize(dfDIJoystick2),225dfDIJoystick2226};227228// Convert a DirectInput return code to a text message229static bool SetDIerror(const char *function, HRESULT code)230{231return SDL_SetError("%s() DirectX error 0x%8.8lx", function, code);232}233234static bool SDL_IsXInputDevice(Uint16 vendor_id, Uint16 product_id, const char *hidPath)235{236#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT)237SDL_GamepadType type;238239// XInput and RawInput backends will pick up XInput-compatible devices240if (!SDL_XINPUT_Enabled()241#ifdef SDL_JOYSTICK_RAWINPUT242&& !RAWINPUT_IsEnabled()243#endif244) {245return false;246}247248// If device path contains "IG_" then its an XInput device249// See: https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput250if (SDL_strstr(hidPath, "IG_") != NULL) {251return true;252}253254type = SDL_GetGamepadTypeFromVIDPID(vendor_id, product_id, NULL, false);255if (type == SDL_GAMEPAD_TYPE_XBOX360 ||256type == SDL_GAMEPAD_TYPE_XBOXONE ||257(vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) {258return true;259}260#endif // SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT261262return false;263}264265static bool QueryDeviceName(LPDIRECTINPUTDEVICE8 device, Uint16 vendor_id, Uint16 product_id, char **manufacturer_string, char **product_string)266{267DIPROPSTRING dipstr;268269if (!device || !manufacturer_string || !product_string) {270return false;271}272273#ifdef SDL_JOYSTICK_HIDAPI274*manufacturer_string = HIDAPI_GetDeviceManufacturerName(vendor_id, product_id);275*product_string = HIDAPI_GetDeviceProductName(vendor_id, product_id);276if (*product_string) {277return true;278}279#endif280281dipstr.diph.dwSize = sizeof(dipstr);282dipstr.diph.dwHeaderSize = sizeof(dipstr.diph);283dipstr.diph.dwObj = 0;284dipstr.diph.dwHow = DIPH_DEVICE;285286if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_PRODUCTNAME, &dipstr.diph))) {287return false;288}289290*manufacturer_string = NULL;291*product_string = WIN_StringToUTF8(dipstr.wsz);292293return true;294}295296static bool QueryDevicePath(LPDIRECTINPUTDEVICE8 device, char **device_path)297{298DIPROPGUIDANDPATH dippath;299300if (!device || !device_path) {301return false;302}303304dippath.diph.dwSize = sizeof(dippath);305dippath.diph.dwHeaderSize = sizeof(dippath.diph);306dippath.diph.dwObj = 0;307dippath.diph.dwHow = DIPH_DEVICE;308309if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_GUIDANDPATH, &dippath.diph))) {310return false;311}312313*device_path = WIN_StringToUTF8W(dippath.wszPath);314315// Normalize path to upper case.316SDL_strupr(*device_path);317318return true;319}320321static bool QueryDeviceInfo(LPDIRECTINPUTDEVICE8 device, Uint16 *vendor_id, Uint16 *product_id)322{323DIPROPDWORD dipdw;324325if (!device || !vendor_id || !product_id) {326return false;327}328329dipdw.diph.dwSize = sizeof(dipdw);330dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);331dipdw.diph.dwObj = 0;332dipdw.diph.dwHow = DIPH_DEVICE;333dipdw.dwData = 0;334335if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_VIDPID, &dipdw.diph))) {336return false;337}338339*vendor_id = LOWORD(dipdw.dwData);340*product_id = HIWORD(dipdw.dwData);341342return true;343}344345void FreeRumbleEffectData(DIEFFECT *effect)346{347if (!effect) {348return;349}350SDL_free(effect->rgdwAxes);351SDL_free(effect->rglDirection);352SDL_free(effect->lpvTypeSpecificParams);353SDL_free(effect);354}355356DIEFFECT *CreateRumbleEffectData(Sint16 magnitude)357{358DIEFFECT *effect;359DIPERIODIC *periodic;360361// Create the effect362effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));363if (!effect) {364return NULL;365}366effect->dwSize = sizeof(*effect);367effect->dwGain = 10000;368effect->dwFlags = DIEFF_OBJECTOFFSETS;369effect->dwDuration = SDL_MAX_RUMBLE_DURATION_MS * 1000; // In microseconds.370effect->dwTriggerButton = DIEB_NOTRIGGER;371372effect->cAxes = 2;373effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));374if (!effect->rgdwAxes) {375FreeRumbleEffectData(effect);376return NULL;377}378379effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));380if (!effect->rglDirection) {381FreeRumbleEffectData(effect);382return NULL;383}384effect->dwFlags |= DIEFF_CARTESIAN;385386periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));387if (!periodic) {388FreeRumbleEffectData(effect);389return NULL;390}391periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);392periodic->dwPeriod = 1000000;393394effect->cbTypeSpecificParams = sizeof(*periodic);395effect->lpvTypeSpecificParams = periodic;396397return effect;398}399400bool SDL_DINPUT_JoystickInit(void)401{402HRESULT result;403HINSTANCE instance;404405if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_DIRECTINPUT, true)) {406// In some environments, IDirectInput8_Initialize / _EnumDevices can take a minute even with no controllers.407dinput = NULL;408return true;409}410411result = WIN_CoInitialize();412if (FAILED(result)) {413return SetDIerror("CoInitialize", result);414}415416coinitialized = true;417418result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,419&IID_IDirectInput8, (LPVOID *)&dinput);420421if (FAILED(result)) {422return SetDIerror("CoCreateInstance", result);423}424425// Because we used CoCreateInstance, we need to Initialize it, first.426instance = GetModuleHandle(NULL);427if (!instance) {428IDirectInput8_Release(dinput);429dinput = NULL;430return SDL_SetError("GetModuleHandle() failed with error code %lu.", GetLastError());431}432result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);433434if (FAILED(result)) {435IDirectInput8_Release(dinput);436dinput = NULL;437return SetDIerror("IDirectInput::Initialize", result);438}439return true;440}441442static int GetSteamVirtualGamepadSlot(Uint16 vendor_id, Uint16 product_id, const char *device_path)443{444int slot = -1;445446if (vendor_id == USB_VENDOR_VALVE &&447product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {448(void)SDL_sscanf(device_path, "\\\\?\\HID#VID_28DE&PID_11FF&IG_0%d", &slot);449}450return slot;451}452453// helper function for direct input, gets called for each connected joystick454static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)455{456#define CHECK(expression) \457{ \458if (!(expression)) \459goto err; \460}461JoyStick_DeviceData *pNewJoystick = NULL;462JoyStick_DeviceData *pPrevJoystick = NULL;463Uint16 vendor = 0;464Uint16 product = 0;465Uint16 version = 0;466char *hidPath = NULL;467char *manufacturer_string = NULL;468char *product_string = NULL;469LPDIRECTINPUTDEVICE8 device = NULL;470471// We are only supporting HID devices.472CHECK(pDeviceInstance->dwDevType & DIDEVTYPE_HID);473474CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL)));475CHECK(QueryDevicePath(device, &hidPath));476CHECK(QueryDeviceInfo(device, &vendor, &product));477CHECK(QueryDeviceName(device, vendor, product, &manufacturer_string, &product_string));478479CHECK(!SDL_IsXInputDevice(vendor, product, hidPath));480CHECK(!SDL_ShouldIgnoreJoystick(vendor, product, version, product_string));481CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, product_string));482483pNewJoystick = *(JoyStick_DeviceData **)pContext;484while (pNewJoystick) {485// update GUIDs of joysticks with matching paths, in case they're not open yet486if (SDL_strcmp(pNewJoystick->path, hidPath) == 0) {487// if we are replacing the front of the list then update it488if (pNewJoystick == *(JoyStick_DeviceData **)pContext) {489*(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;490} else if (pPrevJoystick) {491pPrevJoystick->pNext = pNewJoystick->pNext;492}493494// Update with new guid/etc, if it has changed495SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));496497pNewJoystick->pNext = SYS_Joystick;498SYS_Joystick = pNewJoystick;499500pNewJoystick = NULL;501CHECK(FALSE);502}503504pPrevJoystick = pNewJoystick;505pNewJoystick = pNewJoystick->pNext;506}507508pNewJoystick = (JoyStick_DeviceData *)SDL_calloc(1, sizeof(JoyStick_DeviceData));509CHECK(pNewJoystick);510511pNewJoystick->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(vendor, product, hidPath);512SDL_strlcpy(pNewJoystick->path, hidPath, SDL_arraysize(pNewJoystick->path));513SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));514515pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string);516CHECK(pNewJoystick->joystickname);517518if (vendor && product) {519pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, vendor, product, version, manufacturer_string, product_string, 0, 0);520} else {521pNewJoystick->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, version, manufacturer_string, product_string, 0, 0);522}523524WINDOWS_AddJoystickDevice(pNewJoystick);525pNewJoystick = NULL;526527err:528if (pNewJoystick) {529SDL_free(pNewJoystick->joystickname);530SDL_free(pNewJoystick);531}532533SDL_free(hidPath);534SDL_free(manufacturer_string);535SDL_free(product_string);536537if (device) {538IDirectInputDevice8_Release(device);539}540541return DIENUM_CONTINUE; // get next device, please542#undef CHECK543}544545void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)546{547if (!dinput) {548return;549}550551IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickDetectCallback, pContext, DIEDFL_ATTACHEDONLY);552}553554// helper function for direct input, gets called for each connected joystick555typedef struct556{557Uint16 vendor;558Uint16 product;559bool present;560} Joystick_PresentData;561562static BOOL CALLBACK EnumJoystickPresentCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)563{564#define CHECK(expression) \565{ \566if (!(expression)) \567goto err; \568}569Joystick_PresentData *pData = (Joystick_PresentData *)pContext;570Uint16 vendor = 0;571Uint16 product = 0;572LPDIRECTINPUTDEVICE8 device = NULL;573BOOL result = DIENUM_CONTINUE;574575// We are only supporting HID devices.576CHECK(pDeviceInstance->dwDevType & DIDEVTYPE_HID);577578CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL)));579CHECK(QueryDeviceInfo(device, &vendor, &product));580581if (vendor == pData->vendor && product == pData->product) {582pData->present = true;583result = DIENUM_STOP; // found it584}585586err:587if (device) {588IDirectInputDevice8_Release(device);589}590591return result;592#undef CHECK593}594595bool SDL_DINPUT_JoystickPresent(Uint16 vendor_id, Uint16 product_id, Uint16 version_number)596{597Joystick_PresentData data;598599if (!dinput) {600return false;601}602603data.vendor = vendor_id;604data.product = product_id;605data.present = false;606IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickPresentCallback, &data, DIEDFL_ATTACHEDONLY);607return data.present;608}609610static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE pDeviceObject, LPVOID pContext)611{612SDL_Joystick *joystick = (SDL_Joystick *)pContext;613HRESULT result;614input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];615616if (pDeviceObject->dwType & DIDFT_BUTTON) {617in->type = BUTTON;618in->num = (Uint8)joystick->nbuttons;619in->ofs = DIJOFS_BUTTON(in->num);620joystick->nbuttons++;621} else if (pDeviceObject->dwType & DIDFT_POV) {622in->type = HAT;623in->num = (Uint8)joystick->nhats;624in->ofs = DIJOFS_POV(in->num);625joystick->nhats++;626} else if (pDeviceObject->dwType & DIDFT_AXIS) {627DIPROPRANGE diprg;628DIPROPDWORD dilong;629630in->type = AXIS;631in->num = (Uint8)joystick->naxes;632if (SDL_memcmp(&pDeviceObject->guidType, &GUID_XAxis, sizeof(pDeviceObject->guidType)) == 0) {633in->ofs = DIJOFS_X;634} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_YAxis, sizeof(pDeviceObject->guidType)) == 0) {635in->ofs = DIJOFS_Y;636} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_ZAxis, sizeof(pDeviceObject->guidType)) == 0) {637in->ofs = DIJOFS_Z;638} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_RxAxis, sizeof(pDeviceObject->guidType)) == 0) {639in->ofs = DIJOFS_RX;640} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_RyAxis, sizeof(pDeviceObject->guidType)) == 0) {641in->ofs = DIJOFS_RY;642} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_RzAxis, sizeof(pDeviceObject->guidType)) == 0) {643in->ofs = DIJOFS_RZ;644} else if (SDL_memcmp(&pDeviceObject->guidType, &GUID_Slider, sizeof(pDeviceObject->guidType)) == 0) {645in->ofs = DIJOFS_SLIDER(joystick->hwdata->NumSliders);646++joystick->hwdata->NumSliders;647} else {648return DIENUM_CONTINUE; // not an axis we can grok649}650651diprg.diph.dwSize = sizeof(diprg);652diprg.diph.dwHeaderSize = sizeof(diprg.diph);653diprg.diph.dwObj = pDeviceObject->dwType;654diprg.diph.dwHow = DIPH_BYID;655diprg.lMin = SDL_JOYSTICK_AXIS_MIN;656diprg.lMax = SDL_JOYSTICK_AXIS_MAX;657658result =659IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,660DIPROP_RANGE, &diprg.diph);661if (FAILED(result)) {662return DIENUM_CONTINUE; // don't use this axis663}664665// Set dead zone to 0.666dilong.diph.dwSize = sizeof(dilong);667dilong.diph.dwHeaderSize = sizeof(dilong.diph);668dilong.diph.dwObj = pDeviceObject->dwType;669dilong.diph.dwHow = DIPH_BYID;670dilong.dwData = 0;671result =672IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,673DIPROP_DEADZONE, &dilong.diph);674if (FAILED(result)) {675return DIENUM_CONTINUE; // don't use this axis676}677678joystick->naxes++;679} else {680// not supported at this time681return DIENUM_CONTINUE;682}683684joystick->hwdata->NumInputs++;685686if (joystick->hwdata->NumInputs == MAX_INPUTS) {687return DIENUM_STOP; // too many688}689690return DIENUM_CONTINUE;691}692693/* Sort using the data offset into the DInput struct.694* This gives a reasonable ordering for the inputs.695*/696static int SDLCALL SortDevFunc(const void *a, const void *b)697{698const input_t *inputA = (const input_t *)a;699const input_t *inputB = (const input_t *)b;700701if (inputA->ofs < inputB->ofs) {702return -1;703}704if (inputA->ofs > inputB->ofs) {705return 1;706}707return 0;708}709710// Sort the input objects and recalculate the indices for each input.711static void SortDevObjects(SDL_Joystick *joystick)712{713input_t *inputs = joystick->hwdata->Inputs;714Uint8 nButtons = 0;715Uint8 nHats = 0;716Uint8 nAxis = 0;717int n;718719SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);720721for (n = 0; n < joystick->hwdata->NumInputs; n++) {722switch (inputs[n].type) {723case BUTTON:724inputs[n].num = nButtons;725nButtons++;726break;727728case HAT:729inputs[n].num = nHats;730nHats++;731break;732733case AXIS:734inputs[n].num = nAxis;735nAxis++;736break;737}738}739}740741bool SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)742{743HRESULT result;744DIPROPDWORD dipdw;745746joystick->hwdata->buffered = true;747joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);748749SDL_zero(dipdw);750dipdw.diph.dwSize = sizeof(DIPROPDWORD);751dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);752753result =754IDirectInput8_CreateDevice(dinput,755&joystickdevice->dxdevice.guidInstance,756&joystick->hwdata->InputDevice,757NULL);758if (FAILED(result)) {759return SetDIerror("IDirectInput::CreateDevice", result);760}761762/* Acquire shared access. Exclusive access is required for forces,763* though. */764result =765IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->InputDevice, SDL_HelperWindow,766DISCL_EXCLUSIVE |767DISCL_BACKGROUND);768if (FAILED(result)) {769return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);770}771772// Use the extended data structure: DIJOYSTATE2.773result =774IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,775&SDL_c_dfDIJoystick2);776if (FAILED(result)) {777return SetDIerror("IDirectInputDevice8::SetDataFormat", result);778}779780// Get device capabilities781result =782IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,783&joystick->hwdata->Capabilities);784if (FAILED(result)) {785return SetDIerror("IDirectInputDevice8::GetCapabilities", result);786}787788// Force capable?789if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {790result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);791if (FAILED(result)) {792return SetDIerror("IDirectInputDevice8::Acquire", result);793}794795// reset all actuators.796result =797IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice,798DISFFC_RESET);799800/* Not necessarily supported, ignore if not supported.801if (FAILED(result)) {802return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);803}804*/805806result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);807808if (FAILED(result)) {809return SetDIerror("IDirectInputDevice8::Unacquire", result);810}811812/* Turn on auto-centering for a ForceFeedback device (until told813* otherwise). */814dipdw.diph.dwObj = 0;815dipdw.diph.dwHow = DIPH_DEVICE;816dipdw.dwData = DIPROPAUTOCENTER_ON;817818result =819IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,820DIPROP_AUTOCENTER, &dipdw.diph);821822/* Not necessarily supported, ignore if not supported.823if (FAILED(result)) {824return SetDIerror("IDirectInputDevice8::SetProperty", result);825}826*/827828SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);829}830831// What buttons and axes does it have?832IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,833EnumDevObjectsCallback, joystick,834DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);835836/* Reorder the input objects. Some devices do not report the X axis as837* the first axis, for example. */838SortDevObjects(joystick);839840dipdw.diph.dwObj = 0;841dipdw.diph.dwHow = DIPH_DEVICE;842dipdw.dwData = INPUT_QSIZE;843844// Set the buffer size845result =846IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,847DIPROP_BUFFERSIZE, &dipdw.diph);848849if (result == DI_POLLEDDEVICE) {850/* This device doesn't support buffering, so we're forced851* to use less reliable polling. */852joystick->hwdata->buffered = false;853} else if (FAILED(result)) {854return SetDIerror("IDirectInputDevice8::SetProperty", result);855}856joystick->hwdata->first_update = true;857858// Poll and wait for initial device state to be populated859result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);860if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {861IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);862IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);863}864SDL_Delay(50);865866return true;867}868869static bool SDL_DINPUT_JoystickInitRumble(SDL_Joystick *joystick, Sint16 magnitude)870{871HRESULT result;872873// Reset and then enable actuators874result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);875if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {876result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);877if (SUCCEEDED(result)) {878result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);879}880}881if (FAILED(result)) {882return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);883}884885result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);886if (FAILED(result)) {887return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);888}889890// Create the effect891joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude);892if (!joystick->hwdata->ffeffect) {893return false;894}895896result = IDirectInputDevice8_CreateEffect(joystick->hwdata->InputDevice, &GUID_Sine,897joystick->hwdata->ffeffect, &joystick->hwdata->ffeffect_ref, NULL);898if (FAILED(result)) {899return SetDIerror("IDirectInputDevice8::CreateEffect", result);900}901return true;902}903904bool SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)905{906HRESULT result;907908// Scale and average the two rumble strengths909Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);910911if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {912return SDL_Unsupported();913}914915if (joystick->hwdata->ff_initialized) {916DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);917periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);918919result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));920if (result == DIERR_INPUTLOST) {921result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);922if (SUCCEEDED(result)) {923result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));924}925}926if (FAILED(result)) {927return SetDIerror("IDirectInputDevice8::SetParameters", result);928}929} else {930if (!SDL_DINPUT_JoystickInitRumble(joystick, magnitude)) {931return false;932}933joystick->hwdata->ff_initialized = true;934}935936result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);937if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {938result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);939if (SUCCEEDED(result)) {940result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);941}942}943if (FAILED(result)) {944return SetDIerror("IDirectInputDevice8::Start", result);945}946return true;947}948949static Uint8 TranslatePOV(DWORD value)950{951const Uint8 HAT_VALS[] = {952SDL_HAT_UP,953SDL_HAT_UP | SDL_HAT_RIGHT,954SDL_HAT_RIGHT,955SDL_HAT_DOWN | SDL_HAT_RIGHT,956SDL_HAT_DOWN,957SDL_HAT_DOWN | SDL_HAT_LEFT,958SDL_HAT_LEFT,959SDL_HAT_UP | SDL_HAT_LEFT960};961962if (LOWORD(value) == 0xFFFF) {963return SDL_HAT_CENTERED;964}965966// Round the value up:967value += 4500 / 2;968value %= 36000;969value /= 4500;970971if (value >= 8) {972return SDL_HAT_CENTERED; // shouldn't happen973}974975return HAT_VALS[value];976}977978/* Function to update the state of a joystick - called as a device poll.979* This function shouldn't update the joystick structure directly,980* but instead should call SDL_PrivateJoystick*() to deliver events981* and update joystick device state.982*/983static void UpdateDINPUTJoystickState_Polled(SDL_Joystick *joystick)984{985DIJOYSTATE2 state;986HRESULT result;987int i;988Uint64 timestamp = SDL_GetTicksNS();989990result =991IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,992sizeof(DIJOYSTATE2), &state);993if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {994IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);995result =996IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,997sizeof(DIJOYSTATE2), &state);998}9991000if (result != DI_OK) {1001return;1002}10031004// Set each known axis, button and POV.1005for (i = 0; i < joystick->hwdata->NumInputs; ++i) {1006const input_t *in = &joystick->hwdata->Inputs[i];10071008switch (in->type) {1009case AXIS:1010switch (in->ofs) {1011case DIJOFS_X:1012SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lX);1013break;1014case DIJOFS_Y:1015SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lY);1016break;1017case DIJOFS_Z:1018SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lZ);1019break;1020case DIJOFS_RX:1021SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lRx);1022break;1023case DIJOFS_RY:1024SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lRy);1025break;1026case DIJOFS_RZ:1027SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.lRz);1028break;1029case DIJOFS_SLIDER(0):1030SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.rglSlider[0]);1031break;1032case DIJOFS_SLIDER(1):1033SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)state.rglSlider[1]);1034break;1035}1036break;10371038case BUTTON:1039SDL_SendJoystickButton(timestamp, joystick, in->num,1040(state.rgbButtons[in->ofs - DIJOFS_BUTTON0] != 0));1041break;1042case HAT:1043{1044Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);1045SDL_SendJoystickHat(timestamp, joystick, in->num, pos);1046break;1047}1048}1049}1050}10511052static void UpdateDINPUTJoystickState_Buffered(SDL_Joystick *joystick)1053{1054int i;1055HRESULT result;1056DWORD numevents;1057DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];1058Uint64 timestamp = SDL_GetTicksNS();10591060numevents = INPUT_QSIZE;1061result =1062IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,1063sizeof(DIDEVICEOBJECTDATA), evtbuf,1064&numevents, 0);1065if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {1066IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);1067result =1068IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,1069sizeof(DIDEVICEOBJECTDATA),1070evtbuf, &numevents, 0);1071}10721073// Handle the events or punt1074if (FAILED(result)) {1075return;1076}10771078for (i = 0; i < (int)numevents; ++i) {1079int j;10801081for (j = 0; j < joystick->hwdata->NumInputs; ++j) {1082const input_t *in = &joystick->hwdata->Inputs[j];10831084if (evtbuf[i].dwOfs != in->ofs) {1085continue;1086}10871088switch (in->type) {1089case AXIS:1090SDL_SendJoystickAxis(timestamp, joystick, in->num, (Sint16)evtbuf[i].dwData);1091break;1092case BUTTON:1093SDL_SendJoystickButton(timestamp, joystick, in->num,1094(evtbuf[i].dwData != 0));1095break;1096case HAT:1097{1098Uint8 pos = TranslatePOV(evtbuf[i].dwData);1099SDL_SendJoystickHat(timestamp, joystick, in->num, pos);1100} break;1101}1102}1103}11041105if (result == DI_BUFFEROVERFLOW) {1106/* Our buffer wasn't big enough to hold all the queued events,1107* so poll the device to make sure we have the complete state.1108*/1109UpdateDINPUTJoystickState_Polled(joystick);1110}1111}11121113void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick)1114{1115HRESULT result;11161117result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);1118if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {1119IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);1120IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);1121}11221123if (joystick->hwdata->first_update) {1124// Poll to get the initial state of the joystick1125UpdateDINPUTJoystickState_Polled(joystick);1126joystick->hwdata->first_update = false;1127return;1128}11291130if (joystick->hwdata->buffered ) {1131UpdateDINPUTJoystickState_Buffered(joystick);1132} else {1133UpdateDINPUTJoystickState_Polled(joystick);1134}1135}11361137void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)1138{1139if (joystick->hwdata->ffeffect_ref) {1140IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);1141joystick->hwdata->ffeffect_ref = NULL;1142}1143if (joystick->hwdata->ffeffect) {1144FreeRumbleEffectData(joystick->hwdata->ffeffect);1145joystick->hwdata->ffeffect = NULL;1146}1147IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);1148IDirectInputDevice8_Release(joystick->hwdata->InputDevice);1149joystick->hwdata->ff_initialized = false;1150}11511152void SDL_DINPUT_JoystickQuit(void)1153{1154if (dinput != NULL) {1155IDirectInput8_Release(dinput);1156dinput = NULL;1157}11581159if (coinitialized) {1160WIN_CoUninitialize();1161coinitialized = false;1162}1163}11641165#else // !SDL_JOYSTICK_DINPUT11661167typedef struct JoyStick_DeviceData JoyStick_DeviceData;11681169bool SDL_DINPUT_JoystickInit(void)1170{1171return true;1172}11731174void SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)1175{1176}11771178bool SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)1179{1180return false;1181}11821183bool SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice)1184{1185return SDL_Unsupported();1186}11871188bool SDL_DINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1189{1190return SDL_Unsupported();1191}11921193void SDL_DINPUT_JoystickUpdate(SDL_Joystick *joystick)1194{1195}11961197void SDL_DINPUT_JoystickClose(SDL_Joystick *joystick)1198{1199}12001201void SDL_DINPUT_JoystickQuit(void)1202{1203}12041205#endif // SDL_JOYSTICK_DINPUT120612071208