1 /******************************************************************************/
2 /* Mednafen - Multi-system Emulator                                           */
3 /******************************************************************************/
4 /* Joystick_XInput.cpp:
5 **  Copyright (C) 2012-2018 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 // For future reference: XInputGetState(Ex) is reported to have a very high overhead
23 // when the controller is disconnected.
24 
25 #include "main.h"
26 #include "input.h"
27 #include "Joystick.h"
28 #include "Joystick_XInput.h"
29 
30 #include <windows.h>
31 #include <windowsx.h>
32 #include <xinput.h>
33 
34 struct XInputFuncPointers
35 {
36  void WINAPI (*p_XInputEnable)(BOOL) = nullptr;
37  DWORD WINAPI (*p_XInputSetState)(DWORD, XINPUT_VIBRATION*) = nullptr;
38  DWORD WINAPI (*p_XInputGetState)(DWORD, XINPUT_STATE*) = nullptr;	// Pointer to XInputGetState or XInputGetStateEx(if available).
39  DWORD WINAPI (*p_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = nullptr;
40 };
41 
42 class Joystick_XInput : public Joystick
43 {
44  public:
45 
46  Joystick_XInput(unsigned index, const XINPUT_CAPABILITIES &caps_in, const XInputFuncPointers* xfps_in);
47  ~Joystick_XInput();
48 
49  virtual void SetRumble(uint8 weak_intensity, uint8 strong_intensity);
50  virtual bool IsAxisButton(unsigned axis);
51  void UpdateInternal(void);
52 
53  private:
54  const unsigned joy_index;
55  const XINPUT_CAPABILITIES caps;
56  const XInputFuncPointers *xfps;
57 };
58 
Joystick_XInput(unsigned index,const XINPUT_CAPABILITIES & caps_in,const XInputFuncPointers * xfps_in)59 Joystick_XInput::Joystick_XInput(unsigned index, const XINPUT_CAPABILITIES &caps_in, const XInputFuncPointers* xfps_in) : joy_index(index), caps(caps_in), xfps(xfps_in)
60 {
61  num_buttons = sizeof(((XINPUT_GAMEPAD*)0)->wButtons) * 8;
62  num_axes = 6;
63  num_rel_axes = 0;
64 
65  button_state.resize(num_buttons);
66  axis_state.resize(num_axes);
67 
68  id_09x = (caps.Type << 24) | (caps.SubType << 16);	// Don't include the XInput index in the id, it'll just cause problems, especially when multiple different subtypes of controllers are connected. | (index << 8);
69 
70  // Leave first 8 bytes as 0 to reduce probability of collisions with DirectInput GUIDs?
71  MDFN_en64msb(&id[0], 0);
72  id[8] = caps.Type;
73  id[9] = caps.SubType;
74  MDFN_en16msb(&id[10], caps.Flags);
75  MDFN_en16msb(&id[12], caps.Gamepad.wButtons);
76  MDFN_en16msb(&id[14], 0);
77 
78  const char* name_cs = "XInput Unknown Controller";
79  if(caps.Type == XINPUT_DEVTYPE_GAMEPAD)
80  {
81   switch(caps.SubType)
82   {
83    default: break;
84    case XINPUT_DEVSUBTYPE_GAMEPAD: name_cs = "XInput Gamepad"; break;
85    case XINPUT_DEVSUBTYPE_WHEEL: name_cs = "XInput Wheel"; break;
86    case XINPUT_DEVSUBTYPE_ARCADE_STICK: name_cs = "XInput Arcade Stick"; break;
87 #ifdef XINPUT_DEVSUBTYPE_FLIGHT_STICK
88    case XINPUT_DEVSUBTYPE_FLIGHT_STICK: name_cs = "XInput Flight Stick"; break;
89 #endif
90    case XINPUT_DEVSUBTYPE_DANCE_PAD: name_cs = "XInput Dance Pad"; break;
91 
92 #ifdef XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
93    case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE:
94 #endif
95 #ifdef XINPUT_DEVSUBTYPE_GUITAR_BASS
96    case XINPUT_DEVSUBTYPE_GUITAR_BASS:
97 #endif
98    case XINPUT_DEVSUBTYPE_GUITAR: name_cs = "XInput Guitar"; break;
99 
100    case XINPUT_DEVSUBTYPE_DRUM_KIT: name_cs = "XInput Drum Kit"; break;
101 #ifdef XINPUT_DEVSUBTYPE_ARCADE_PAD
102    case XINPUT_DEVSUBTYPE_ARCADE_PAD: name_cs = "XInput Arcade Pad"; break;
103 #endif
104   }
105  }
106 
107  name = name_cs;
108 }
109 
110 
~Joystick_XInput()111 Joystick_XInput::~Joystick_XInput()
112 {
113 
114 }
115 
IsAxisButton(unsigned axis)116 bool Joystick_XInput::IsAxisButton(unsigned axis)
117 {
118  if(axis >= 4)
119   return(true);
120 
121  return(false);
122 }
123 
UpdateInternal(void)124 void Joystick_XInput::UpdateInternal(void)
125 {
126  XINPUT_STATE joy_state;
127  memset(&joy_state, 0, sizeof(XINPUT_STATE));
128 
129  xfps->p_XInputGetState(joy_index, &joy_state);
130 
131  for(unsigned b = 0; b < num_buttons; b++)
132   button_state[b] = (joy_state.Gamepad.wButtons >> b) & 1;
133 
134  axis_state[0] = std::max<int>(-32767, joy_state.Gamepad.sThumbLX);
135  axis_state[1] = std::max<int>(-32767, joy_state.Gamepad.sThumbLY);
136  axis_state[2] = std::max<int>(-32767, joy_state.Gamepad.sThumbRX);
137  axis_state[3] = std::max<int>(-32767, joy_state.Gamepad.sThumbRY);
138 
139  axis_state[4] = (((unsigned)joy_state.Gamepad.bLeftTrigger * 32767) + 127) / 255;
140  axis_state[5] = (((unsigned)joy_state.Gamepad.bRightTrigger * 32767) + 127) / 255;
141 }
142 
SetRumble(uint8 weak_intensity,uint8 strong_intensity)143 void Joystick_XInput::SetRumble(uint8 weak_intensity, uint8 strong_intensity)
144 {
145  XINPUT_VIBRATION vib;
146 
147  memset(&vib, 0, sizeof(XINPUT_VIBRATION));
148  vib.wLeftMotorSpeed = (((unsigned int)strong_intensity * 65535) + 127) / 255;
149  vib.wRightMotorSpeed = (((unsigned int)weak_intensity * 65535) + 127) / 255;
150  xfps->p_XInputSetState(joy_index, &vib);
151 }
152 
153 class JoystickDriver_XInput : public JoystickDriver
154 {
155  public:
156 
157  JoystickDriver_XInput();
158  virtual ~JoystickDriver_XInput();
159 
160  virtual unsigned NumJoysticks();                       // Cached internally on JoystickDriver instantiation.
161  virtual Joystick *GetJoystick(unsigned index);
162  virtual void UpdateJoysticks(void);
163 
164  private:
165  Joystick_XInput *joys[XUSER_MAX_COUNT];
166  unsigned joy_count = 0;
167 
168 
169  HMODULE dll_handle = nullptr;
170  XInputFuncPointers xfps;
171 };
172 
173 template<typename T>
GetXIPA(HMODULE dll_handle,T & pf,const char * name)174 bool GetXIPA(HMODULE dll_handle, T& pf, const char *name)
175 {
176  pf = (T)GetProcAddress(dll_handle, name);
177  return(pf != NULL);
178 }
179 
JoystickDriver_XInput()180 JoystickDriver_XInput::JoystickDriver_XInput()
181 {
182  if((dll_handle = LoadLibrary("xinput1_3.dll")) == NULL)
183  {
184   if((dll_handle = LoadLibrary("xinput1_4.dll")) == NULL)
185   {
186    if((dll_handle = LoadLibrary("xinput9_1_0.dll")) == NULL)
187    {
188     return;
189    }
190   }
191  }
192 
193  // 9.1.0 supposedly lacks XInputEnable()
194  xfps.p_XInputEnable = NULL;
195  GetXIPA(dll_handle, xfps.p_XInputEnable, "XInputEnable");
196 
197  if(!GetXIPA(dll_handle, xfps.p_XInputSetState, "XInputSetState") ||
198     (!GetXIPA(dll_handle, xfps.p_XInputGetState, (const char *)100/*"XInputGetStateEx"*/) && !GetXIPA(dll_handle, xfps.p_XInputGetState, "XInputGetState")) ||
199     !GetXIPA(dll_handle, xfps.p_XInputGetCapabilities, "XInputGetCapabilities"))
200  {
201   FreeLibrary(dll_handle);
202   return;
203  }
204 
205  if(xfps.p_XInputEnable)
206   xfps.p_XInputEnable(TRUE);
207 
208  for(unsigned i = 0; i < XUSER_MAX_COUNT; i++)
209  {
210   joys[i] = NULL;
211   try
212   {
213    XINPUT_CAPABILITIES caps;
214 
215    if(xfps.p_XInputGetCapabilities(i, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS)
216    {
217     joys[joy_count] = new Joystick_XInput(i, caps, &xfps);
218     joy_count++; // joys[joy_count++] would not be exception-safe.
219    }
220   }
221   catch(std::exception &e)
222   {
223    MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
224   }
225  }
226 }
227 
~JoystickDriver_XInput()228 JoystickDriver_XInput::~JoystickDriver_XInput()
229 {
230  if(xfps.p_XInputEnable)
231   xfps.p_XInputEnable(FALSE);
232 
233  for(unsigned i = 0; i < joy_count; i++)
234  {
235   if(joys[i])
236   {
237    delete joys[i];
238    joys[i] = NULL;
239   }
240  }
241 
242  joy_count = 0;
243  if(dll_handle != NULL)
244  {
245   FreeLibrary(dll_handle);
246   dll_handle = NULL;
247  }
248 }
249 
NumJoysticks(void)250 unsigned JoystickDriver_XInput::NumJoysticks(void)
251 {
252  return joy_count;
253 }
254 
GetJoystick(unsigned index)255 Joystick *JoystickDriver_XInput::GetJoystick(unsigned index)
256 {
257  return joys[index];
258 }
259 
UpdateJoysticks(void)260 void JoystickDriver_XInput::UpdateJoysticks(void)
261 {
262  for(unsigned i = 0; i < joy_count; i++)
263   joys[i]->UpdateInternal();
264 }
265 
JoystickDriver_XInput_New(void)266 JoystickDriver *JoystickDriver_XInput_New(void)
267 {
268  JoystickDriver_XInput* jdxi = new JoystickDriver_XInput();
269 
270  if(!jdxi->NumJoysticks())
271  {
272   delete jdxi;
273   jdxi = NULL;
274  }
275 
276  return(jdxi);
277 }
278