BsWin32Input.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Input/BsInput.h"
  4. #include "Error/BsException.h"
  5. #include "Win32/BsWin32Input.h"
  6. #include "Input/BsMouse.h"
  7. #include "Input/BsKeyboard.h"
  8. #include "Input/BsGamepad.h"
  9. namespace bs
  10. {
  11. BOOL CALLBACK _DIEnumDevCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
  12. {
  13. InputPrivateData* data = (InputPrivateData*)(pvRef);
  14. if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_JOYSTICK ||
  15. GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_GAMEPAD ||
  16. GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_1STPERSON ||
  17. GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_DRIVING ||
  18. GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_FLIGHT)
  19. {
  20. GamepadInfo gamepadInfo;
  21. gamepadInfo.name = lpddi->tszInstanceName;
  22. gamepadInfo.guidInstance = lpddi->guidInstance;
  23. gamepadInfo.guidProduct = lpddi->guidProduct;
  24. gamepadInfo.id = (UINT32)data->gamepadInfos.size();
  25. gamepadInfo.isXInput = false;
  26. gamepadInfo.xInputDev = 0;
  27. data->gamepadInfos.push_back(gamepadInfo);
  28. }
  29. return DIENUM_CONTINUE;
  30. }
  31. void CheckXInputDevices(Vector<GamepadInfo>& infos)
  32. {
  33. if (infos.size() == 0)
  34. return;
  35. HRESULT hr = CoInitialize(nullptr);
  36. bool cleanupCOM = SUCCEEDED(hr);
  37. BSTR classNameSpace = SysAllocString(L"\\\\.\\root\\cimv2");
  38. BSTR className = SysAllocString(L"Win32_PNPEntity");
  39. BSTR deviceID = SysAllocString(L"DeviceID");
  40. IWbemServices* IWbemServices = nullptr;
  41. IEnumWbemClassObject* enumDevices = nullptr;
  42. IWbemClassObject* devices[20] = { 0 };
  43. // Create WMI
  44. IWbemLocator* IWbemLocator = nullptr;
  45. hr = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&IWbemLocator);
  46. if (FAILED(hr) || IWbemLocator == nullptr)
  47. goto cleanup;
  48. if (classNameSpace == nullptr)
  49. goto cleanup;
  50. if (className == nullptr)
  51. goto cleanup;
  52. if (deviceID == nullptr)
  53. goto cleanup;
  54. // Connect to WMI
  55. hr = IWbemLocator->ConnectServer(classNameSpace, nullptr, nullptr, 0L, 0L, nullptr, nullptr, &IWbemServices);
  56. if (FAILED(hr) || IWbemServices == nullptr)
  57. goto cleanup;
  58. // Switch security level to IMPERSONATE
  59. CoSetProxyBlanket(IWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
  60. hr = IWbemServices->CreateInstanceEnum(className, 0, nullptr, &enumDevices);
  61. if (FAILED(hr) || enumDevices == nullptr)
  62. goto cleanup;
  63. // Loop over all devices
  64. for (;; )
  65. {
  66. DWORD numDevices = 0;
  67. hr = enumDevices->Next(5000, 20, devices, &numDevices);
  68. if (FAILED(hr))
  69. goto cleanup;
  70. if (numDevices == 0)
  71. break;
  72. for (DWORD i = 0; i < numDevices; i++)
  73. {
  74. // For each device, get its device ID
  75. VARIANT var;
  76. hr = devices[i]->Get(deviceID, 0L, &var, nullptr, nullptr);
  77. if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != nullptr)
  78. {
  79. // Check if the device ID contains "IG_". If it does, then it's an XInput device
  80. if (wcsstr(var.bstrVal, L"IG_"))
  81. {
  82. // If it does, then get the VID/PID from var.bstrVal
  83. DWORD dwPid = 0, dwVid = 0;
  84. WCHAR* strVid = wcsstr(var.bstrVal, L"VID_");
  85. if (strVid && swscanf_s(strVid, L"VID_%4X", &dwVid) != 1)
  86. dwVid = 0;
  87. WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
  88. if (strPid && swscanf_s(strPid, L"PID_%4X", &dwPid) != 1)
  89. dwPid = 0;
  90. // Compare the VID/PID to the DInput device
  91. DWORD dwVidPid = MAKELONG(dwVid, dwPid);
  92. for (auto entry : infos)
  93. {
  94. if (dwVidPid == entry.guidProduct.Data1)
  95. {
  96. entry.isXInput = true;
  97. entry.xInputDev = (int)entry.id; // Note: These might not match and I might need to get the XInput id differently
  98. }
  99. }
  100. }
  101. }
  102. devices[i]->Release();
  103. }
  104. }
  105. cleanup:
  106. if (classNameSpace)
  107. SysFreeString(classNameSpace);
  108. if (deviceID)
  109. SysFreeString(deviceID);
  110. if (className)
  111. SysFreeString(className);
  112. for (DWORD i = 0; i < 20; i++)
  113. devices[i]->Release();
  114. enumDevices->Release();
  115. IWbemLocator->Release();
  116. IWbemServices->Release();
  117. if (cleanupCOM)
  118. CoUninitialize();
  119. }
  120. void Input::initRawInput()
  121. {
  122. mPlatformData = bs_new<InputPrivateData>();
  123. if (IsWindow((HWND)mWindowHandle) == 0)
  124. BS_EXCEPT(InvalidStateException, "RawInputManager failed to initialized. Invalid HWND provided.")
  125. HINSTANCE hInst = GetModuleHandle(0);
  126. HRESULT hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&mPlatformData->directInput, nullptr);
  127. if (FAILED(hr))
  128. BS_EXCEPT(InternalErrorException, "Unable to initialize DirectInput.");
  129. mPlatformData->kbSettings = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;
  130. mPlatformData->mouseSettings = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE;
  131. // Enumerate all attached devices
  132. // Note: Only enumerating gamepads, assuming there is 1 keyboard and 1 mouse
  133. mPlatformData->directInput->EnumDevices(NULL, _DIEnumDevCallback, mPlatformData, DIEDFL_ATTACHEDONLY);
  134. for (UINT32 i = 0; i < 4; ++i)
  135. {
  136. XINPUT_STATE state;
  137. if (XInputGetState(i, &state) != ERROR_DEVICE_NOT_CONNECTED)
  138. {
  139. CheckXInputDevices(mPlatformData->gamepadInfos);
  140. break;
  141. }
  142. }
  143. if (getDeviceCount(InputDevice::Keyboard) > 0)
  144. mKeyboard = bs_new<Keyboard>("Keyboard", this);
  145. if (getDeviceCount(InputDevice::Mouse) > 0)
  146. mMouse = bs_new<Mouse>("Mouse", this);
  147. UINT32 numGamepads = getDeviceCount(InputDevice::Gamepad);
  148. for (UINT32 i = 0; i < numGamepads; i++)
  149. mGamepads.push_back(bs_new<Gamepad>(mPlatformData->gamepadInfos[i].name, mPlatformData->gamepadInfos[i], this));
  150. }
  151. void Input::cleanUpRawInput()
  152. {
  153. if (mMouse != nullptr)
  154. bs_delete(mMouse);
  155. if (mKeyboard != nullptr)
  156. bs_delete(mKeyboard);
  157. for (auto& gamepad : mGamepads)
  158. bs_delete(gamepad);
  159. mPlatformData->directInput->Release();
  160. bs_delete(mPlatformData);
  161. }
  162. UINT32 Input::getDeviceCount(InputDevice device) const
  163. {
  164. switch(device)
  165. {
  166. case InputDevice::Keyboard: return 1;
  167. case InputDevice::Mouse: return 1;
  168. case InputDevice::Gamepad: return (UINT32)mPlatformData->gamepadInfos.size();
  169. default:
  170. case InputDevice::Count: return 0;
  171. }
  172. }
  173. }