DInputJoystick.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright 2010 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "InputCommon/ControllerInterface/DInput/DInputJoystick.h"
  4. #include <algorithm>
  5. #include <limits>
  6. #include <mutex>
  7. #include <set>
  8. #include <type_traits>
  9. #include <fmt/format.h>
  10. #include "Common/HRWrap.h"
  11. #include "Common/Logging/Log.h"
  12. #include "InputCommon/ControllerInterface/ControllerInterface.h"
  13. #include "InputCommon/ControllerInterface/DInput/DInput.h"
  14. #include "InputCommon/ControllerInterface/DInput/XInputFilter.h"
  15. namespace ciface::DInput
  16. {
  17. constexpr DWORD DATA_BUFFER_SIZE = 32;
  18. struct GUIDComparator
  19. {
  20. bool operator()(const GUID& left, const GUID& right) const
  21. {
  22. static_assert(std::is_trivially_copyable_v<GUID>);
  23. return memcmp(&left, &right, sizeof(left)) < 0;
  24. }
  25. };
  26. static std::set<GUID, GUIDComparator> s_guids_in_use;
  27. static std::mutex s_guids_mutex;
  28. void InitJoystick(IDirectInput8* const idi8, HWND hwnd)
  29. {
  30. std::list<DIDEVICEINSTANCE> joysticks;
  31. idi8->EnumDevices(DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks,
  32. DIEDFL_ATTACHEDONLY);
  33. std::unordered_set<DWORD> xinput_guids = GetXInputGUIDS();
  34. for (DIDEVICEINSTANCE& joystick : joysticks)
  35. {
  36. // Skip XInput Devices
  37. if (xinput_guids.contains(joystick.guidProduct.Data1))
  38. {
  39. continue;
  40. }
  41. // Skip devices we are already using.
  42. {
  43. std::lock_guard lk(s_guids_mutex);
  44. if (s_guids_in_use.contains(joystick.guidInstance))
  45. {
  46. continue;
  47. }
  48. }
  49. LPDIRECTINPUTDEVICE8 js_device;
  50. // Don't print any warnings on failure
  51. if (SUCCEEDED(idi8->CreateDevice(joystick.guidInstance, &js_device, nullptr)))
  52. {
  53. if (SUCCEEDED(js_device->SetDataFormat(&c_dfDIJoystick)))
  54. {
  55. HRESULT hr = js_device->SetCooperativeLevel(GetAncestor(hwnd, GA_ROOT),
  56. DISCL_BACKGROUND | DISCL_EXCLUSIVE);
  57. if (FAILED(hr))
  58. {
  59. WARN_LOG_FMT(CONTROLLERINTERFACE,
  60. "DInput: Failed to acquire device exclusively. Force feedback will be "
  61. "unavailable. {}",
  62. Common::HRWrap(hr));
  63. // Fall back to non-exclusive mode, with no rumble
  64. if (FAILED(
  65. js_device->SetCooperativeLevel(nullptr, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
  66. {
  67. js_device->Release();
  68. continue;
  69. }
  70. }
  71. auto js = std::make_shared<Joystick>(js_device);
  72. // only add if it has some inputs/outputs.
  73. // Don't even add it to our static list in case we first created it without a window handle,
  74. // failing to get exclusive mode, and then later managed to obtain it, which mean it
  75. // could now have some outputs if it didn't before.
  76. if (js->Inputs().size() || js->Outputs().size())
  77. {
  78. if (g_controller_interface.AddDevice(std::move(js)))
  79. {
  80. std::lock_guard lk(s_guids_mutex);
  81. s_guids_in_use.insert(joystick.guidInstance);
  82. }
  83. }
  84. }
  85. else
  86. {
  87. js_device->Release();
  88. }
  89. }
  90. }
  91. }
  92. Joystick::Joystick(const LPDIRECTINPUTDEVICE8 device) : m_device(device)
  93. {
  94. // seems this needs to be done before GetCapabilities
  95. // polled or buffered data
  96. DIPROPDWORD dipdw;
  97. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  98. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  99. dipdw.diph.dwObj = 0;
  100. dipdw.diph.dwHow = DIPH_DEVICE;
  101. dipdw.dwData = DATA_BUFFER_SIZE;
  102. // set the buffer size,
  103. // if we can't set the property, we can't use buffered data
  104. m_buffered = SUCCEEDED(m_device->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph));
  105. // seems this needs to be done after SetProperty of buffer size
  106. m_device->Acquire();
  107. // get joystick caps
  108. DIDEVCAPS js_caps;
  109. js_caps.dwSize = sizeof(js_caps);
  110. if (FAILED(m_device->GetCapabilities(&js_caps)))
  111. return;
  112. // max of 32 buttons and 4 hats / the limit of the data format I am using
  113. js_caps.dwButtons = std::min((DWORD)32, js_caps.dwButtons);
  114. js_caps.dwPOVs = std::min((DWORD)4, js_caps.dwPOVs);
  115. // m_must_poll = (js_caps.dwFlags & DIDC_POLLEDDATAFORMAT) != 0;
  116. // buttons
  117. for (u8 i = 0; i != js_caps.dwButtons; ++i)
  118. AddInput(new Button(i, m_state_in.rgbButtons[i]));
  119. // hats
  120. for (u8 i = 0; i != js_caps.dwPOVs; ++i)
  121. {
  122. // each hat gets 4 input instances associated with it, (up down left right)
  123. for (u8 d = 0; d != 4; ++d)
  124. AddInput(new Hat(i, m_state_in.rgdwPOV[i], d));
  125. }
  126. // get up to 6 axes and 2 sliders
  127. DIPROPRANGE range;
  128. range.diph.dwSize = sizeof(range);
  129. range.diph.dwHeaderSize = sizeof(range.diph);
  130. range.diph.dwHow = DIPH_BYOFFSET;
  131. // screw EnumObjects, just go through all the axis offsets and try to GetProperty
  132. // this should be more foolproof, less code, and probably faster
  133. for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0) / sizeof(LONG); ++offset)
  134. {
  135. range.diph.dwObj = offset * sizeof(LONG);
  136. // Try to set a range with 16 bits of precision:
  137. range.lMin = std::numeric_limits<s16>::min();
  138. range.lMax = std::numeric_limits<s16>::max();
  139. m_device->SetProperty(DIPROP_RANGE, &range.diph);
  140. // Not all devices support setting DIPROP_RANGE so we must GetProperty right back.
  141. // This also checks that the axis is present.
  142. // Note: Even though not all devices support setting DIPROP_RANGE, some require it.
  143. if (SUCCEEDED(m_device->GetProperty(DIPROP_RANGE, &range.diph)))
  144. {
  145. const LONG base = (range.lMin + range.lMax) / 2;
  146. const LONG& ax = (&m_state_in.lX)[offset];
  147. // each axis gets a negative and a positive input instance associated with it
  148. AddFullAnalogSurfaceInputs(new Axis(offset, ax, base, range.lMin - base),
  149. new Axis(offset, ax, base, range.lMax - base));
  150. }
  151. }
  152. // Force feedback:
  153. std::list<DIDEVICEOBJECTINSTANCE> objects;
  154. if (SUCCEEDED(m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS)))
  155. {
  156. const int num_ff_axes = std::ranges::count_if(
  157. objects, [](const auto& pdidoi) { return (pdidoi.dwFlags & DIDOI_FFACTUATOR) != 0; });
  158. InitForceFeedback(m_device, num_ff_axes);
  159. }
  160. // Set hats to center:
  161. // "The center position is normally reported as -1" -MSDN
  162. std::ranges::fill(m_state_in.rgdwPOV, -1);
  163. }
  164. Joystick::~Joystick()
  165. {
  166. DIDEVICEINSTANCE info = {};
  167. info.dwSize = sizeof(info);
  168. if (SUCCEEDED(m_device->GetDeviceInfo(&info)))
  169. {
  170. std::lock_guard lk(s_guids_mutex);
  171. s_guids_in_use.erase(info.guidInstance);
  172. }
  173. else
  174. {
  175. ERROR_LOG_FMT(CONTROLLERINTERFACE, "DInputJoystick: GetDeviceInfo failed.");
  176. }
  177. DeInitForceFeedback();
  178. m_device->Unacquire();
  179. m_device->Release();
  180. }
  181. std::string Joystick::GetName() const
  182. {
  183. return GetDeviceName(m_device);
  184. }
  185. std::string Joystick::GetSource() const
  186. {
  187. return DINPUT_SOURCE_NAME;
  188. }
  189. bool Joystick::IsValid() const
  190. {
  191. return SUCCEEDED(m_device->Acquire());
  192. }
  193. Core::DeviceRemoval Joystick::UpdateInput()
  194. {
  195. HRESULT hr = 0;
  196. // just always poll,
  197. // MSDN says if this isn't needed it doesn't do anything
  198. m_device->Poll();
  199. if (m_buffered)
  200. {
  201. DIDEVICEOBJECTDATA evtbuf[DATA_BUFFER_SIZE];
  202. DWORD numevents = DATA_BUFFER_SIZE;
  203. hr = m_device->GetDeviceData(sizeof(*evtbuf), evtbuf, &numevents, 0);
  204. if (SUCCEEDED(hr))
  205. {
  206. for (LPDIDEVICEOBJECTDATA evt = evtbuf; evt != (evtbuf + numevents); ++evt)
  207. {
  208. // all the buttons are at the end of the data format
  209. // they are bytes rather than longs
  210. if (evt->dwOfs < DIJOFS_BUTTON(0))
  211. *(DWORD*)(((BYTE*)&m_state_in) + evt->dwOfs) = evt->dwData;
  212. else
  213. ((BYTE*)&m_state_in)[evt->dwOfs] = (BYTE)evt->dwData;
  214. }
  215. // seems like this needs to be done maybe...
  216. if (DI_BUFFEROVERFLOW == hr)
  217. hr = m_device->GetDeviceState(sizeof(m_state_in), &m_state_in);
  218. }
  219. }
  220. else
  221. {
  222. hr = m_device->GetDeviceState(sizeof(m_state_in), &m_state_in);
  223. }
  224. // try reacquire if input lost
  225. if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr)
  226. m_device->Acquire();
  227. return Core::DeviceRemoval::Keep;
  228. }
  229. // get name
  230. std::string Joystick::Button::GetName() const
  231. {
  232. return fmt::format("Button {}", m_index);
  233. }
  234. std::string Joystick::Axis::GetName() const
  235. {
  236. const char sign = m_range < 0 ? '-' : '+';
  237. if (m_index < 6) // axis
  238. return fmt::format("Axis {:c}{}{:c}", 'X' + m_index % 3, m_index > 2 ? "r" : "", sign);
  239. else // slider
  240. return fmt::format("Slider {}{:c}", m_index - 6, sign);
  241. }
  242. std::string Joystick::Hat::GetName() const
  243. {
  244. return fmt::format("Hat {} {:c}", m_index, "NESW"[m_direction]);
  245. }
  246. // get / set state
  247. ControlState Joystick::Axis::GetState() const
  248. {
  249. return ControlState(m_axis - m_base) / m_range;
  250. }
  251. ControlState Joystick::Button::GetState() const
  252. {
  253. return ControlState(m_button > 0);
  254. }
  255. ControlState Joystick::Hat::GetState() const
  256. {
  257. // "Some drivers report the centered position of the POV indicator as 65,535.
  258. // Determine whether the indicator is centered as follows" -MSDN
  259. const bool is_centered = (0xffff == LOWORD(m_hat));
  260. if (is_centered)
  261. return 0;
  262. return (std::abs(int(m_hat / 4500 - m_direction * 2 + 8) % 8 - 4) > 2);
  263. }
  264. } // namespace ciface::DInput