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