1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2011-2017 - Daniel De Matteis
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <stdint.h>
18
19 #include "../../config.def.h"
20
21 #include "../input_driver.h"
22 #include "../../tasks/tasks_internal.h"
23
24 typedef struct
25 {
26 HANDLE id;
27 XINPUT_STATE xstate;
28 bool connected;
29 } xinput_joypad_state;
30
31 /* TODO/FIXME - static globals */
32 static xinput_joypad_state g_xinput_states[DEFAULT_MAX_PADS];
33
xdk_joypad_name(unsigned pad)34 static const char *xdk_joypad_name(unsigned pad)
35 {
36 static const char* const XBOX_CONTROLLER_NAMES[4] =
37 {
38 "XInput Controller (User 1)",
39 "XInput Controller (User 2)",
40 "XInput Controller (User 3)",
41 "XInput Controller (User 4)"
42 };
43 return XBOX_CONTROLLER_NAMES[pad];
44 }
45
xdk_joypad_autodetect_add(unsigned autoconf_pad)46 static void xdk_joypad_autodetect_add(unsigned autoconf_pad)
47 {
48 input_autoconfigure_connect(
49 xdk_joypad_name(autoconf_pad),
50 NULL,
51 xdk_joypad.ident,
52 autoconf_pad,
53 0,
54 0);
55 }
56
xdk_joypad_init(void * data)57 static void *xdk_joypad_init(void *data)
58 {
59 XInitDevices(0, NULL);
60 return (void*)-1;
61 }
62
xdk_joypad_button_state(XINPUT_GAMEPAD * pad,uint16_t btn_word,unsigned port,uint16_t joykey)63 static int16_t xdk_joypad_button_state(
64 XINPUT_GAMEPAD *pad,
65 uint16_t btn_word,
66 unsigned port, uint16_t joykey)
67 {
68 unsigned hat_dir = GET_HAT_DIR(joykey);
69
70 if (hat_dir)
71 {
72 switch (hat_dir)
73 {
74 case HAT_UP_MASK:
75 return (btn_word & XINPUT_GAMEPAD_DPAD_UP);
76 case HAT_DOWN_MASK:
77 return (btn_word & XINPUT_GAMEPAD_DPAD_DOWN);
78 case HAT_LEFT_MASK:
79 return (btn_word & XINPUT_GAMEPAD_DPAD_LEFT);
80 case HAT_RIGHT_MASK:
81 return (btn_word & XINPUT_GAMEPAD_DPAD_RIGHT);
82 default:
83 break;
84 }
85 /* hat requested and no hat button down */
86 }
87 else
88 {
89 switch (joykey)
90 {
91 case RETRO_DEVICE_ID_JOYPAD_A:
92 return (pad->bAnalogButtons[XINPUT_GAMEPAD_B] > XINPUT_GAMEPAD_MAX_CROSSTALK);
93 case RETRO_DEVICE_ID_JOYPAD_B:
94 return (pad->bAnalogButtons[XINPUT_GAMEPAD_A] > XINPUT_GAMEPAD_MAX_CROSSTALK);
95 case RETRO_DEVICE_ID_JOYPAD_Y:
96 return (pad->bAnalogButtons[XINPUT_GAMEPAD_X] > XINPUT_GAMEPAD_MAX_CROSSTALK);
97 case RETRO_DEVICE_ID_JOYPAD_X:
98 return (pad->bAnalogButtons[XINPUT_GAMEPAD_Y] > XINPUT_GAMEPAD_MAX_CROSSTALK)
99 case RETRO_DEVICE_ID_JOYPAD_START:
100 return (pad->wButtons & XINPUT_GAMEPAD_START);
101 case RETRO_DEVICE_ID_JOYPAD_SELECT:
102 return (pad->wButtons & XINPUT_GAMEPAD_BACK);
103 case RETRO_DEVICE_ID_JOYPAD_L3:
104 return (pad->wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
105 case RETRO_DEVICE_ID_JOYPAD_R3:
106 return (pad->wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
107 case RETRO_DEVICE_ID_JOYPAD_L2:
108 return (pad->bAnalogButtons[XINPUT_GAMEPAD_WHITE] > XINPUT_GAMEPAD_MAX_CROSSTALK);
109 case RETRO_DEVICE_ID_JOYPAD_R2:
110 return (pad->bAnalogButtons[XINPUT_GAMEPAD_BLACK] > XINPUT_GAMEPAD_MAX_CROSSTALK);
111 case RETRO_DEVICE_ID_JOYPAD_L:
112 return (pad->bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] > XINPUT_GAMEPAD_MAX_CROSSTALK);
113 case RETRO_DEVICE_ID_JOYPAD_R:
114 return (pad->bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] > XINPUT_GAMEPAD_MAX_CROSSTALK);
115 default:
116 break;
117 }
118 }
119 return 0;
120 }
121
xdk_joypad_button(unsigned port,uint16_t joykey)122 static int16_t xdk_joypad_button(unsigned port, uint16_t joykey)
123 {
124 uint16_t btn_word = 0;
125 XINPUT_GAMEPAD *pad = NULL;
126 if (port >= DEFAULT_MAX_PADS)
127 return 0;
128 pad = &(g_xinput_states[port].xstate.Gamepad);
129 btn_word = pad->wButtons;
130 return xdk_joypad_button_state(pad, btn_word, port, joykey);
131 }
132
xdk_joypad_axis_state(XINPUT_GAMEPAD * pad,unsigned port,uint32_t joyaxis)133 static int16_t xdk_joypad_axis_state(XINPUT_GAMEPAD *pad,
134 unsigned port, uint32_t joyaxis)
135 {
136 int val = 0;
137 int axis = -1;
138 bool is_neg = false;
139 bool is_pos = false;
140
141 if (AXIS_NEG_GET(joyaxis) <= 3)
142 {
143 axis = AXIS_NEG_GET(joyaxis);
144 is_neg = true;
145 }
146 else if (AXIS_POS_GET(joyaxis) <= 5)
147 {
148 axis = AXIS_POS_GET(joyaxis);
149 is_pos = true;
150 }
151 else
152 return 0;
153
154 switch (axis)
155 {
156 case 0:
157 val = pad->sThumbLX;
158 break;
159 case 1:
160 val = pad->sThumbLY;
161 break;
162 case 2:
163 val = pad->sThumbRX;
164 break;
165 case 3:
166 val = pad->sThumbRY;
167 break;
168 }
169
170 if (is_neg && val > 0)
171 return 0;
172 else if (is_pos && val < 0)
173 return 0;
174 /* Clamp to avoid warnings */
175 else if (val == -32768)
176 return -32767;
177 return val;
178 }
179
xdk_joypad_axis(unsigned port,uint32_t joyaxis)180 static int16_t xdk_joypad_axis(unsigned port, uint32_t joyaxis)
181 {
182 XINPUT_GAMEPAD *pad = &(g_xinput_states[port].xstate.Gamepad);
183 if (port >= DEFAULT_MAX_PADS)
184 return 0;
185 return xdk_joypad_axis_state(pad, port, joyaxis);
186 }
187
xdk_joypad_state(rarch_joypad_info_t * joypad_info,const struct retro_keybind * binds,unsigned port)188 static int16_t xdk_joypad_state(
189 rarch_joypad_info_t *joypad_info,
190 const struct retro_keybind *binds,
191 unsigned port)
192 {
193 unsigned i;
194 int16_t ret = 0;
195 XINPUT_GAMEPAD *pad = NULL;
196 uint16_t btn_word = 0;
197 uint16_t port_idx = joypad_info->joy_idx;
198
199 if (port_idx >= DEFAULT_MAX_PADS)
200 return 0;
201
202 pad = &(g_xinput_states[port_idx].xstate.Gamepad);
203 btn_word = pad->wButtons;
204
205 for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
206 {
207 /* Auto-binds are per joypad, not per user. */
208 const uint64_t joykey = (binds[i].joykey != NO_BTN)
209 ? binds[i].joykey : joypad_info->auto_binds[i].joykey;
210 const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
211 ? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
212 if (
213 (uint16_t)joykey != NO_BTN
214 && xdk_joypad_button_state(
215 pad, btn_word, port_idx, (uint16_t)joykey))
216 ret |= ( 1 << i);
217 else if (joyaxis != AXIS_NONE &&
218 ((float)abs(xdk_joypad_axis_state(pad, port_idx, joyaxis))
219 / 0x8000) > joypad_info->axis_threshold)
220 ret |= (1 << i);
221 }
222
223 return ret;
224 }
225
xdk_joypad_poll(void)226 static void xdk_joypad_poll(void)
227 {
228 unsigned port;
229 DWORD dwInsertions, dwRemovals;
230
231 #ifdef __cplusplus
232 XGetDeviceChanges(XDEVICE_TYPE_GAMEPAD,
233 reinterpret_cast<PDWORD>(&dwInsertions),
234 reinterpret_cast<PDWORD>(&dwRemovals));
235 #else
236 XGetDeviceChanges(XDEVICE_TYPE_GAMEPAD,
237 (PDWORD)&dwInsertions,
238 (PDWORD)&dwRemovals);
239 #endif
240
241 for (port = 0; port < DEFAULT_MAX_PADS; port++)
242 {
243 bool device_removed = false;
244 bool device_inserted = false;
245
246 /* handle inserted devices. */
247 /* handle removed devices. */
248 if (dwRemovals & (1 << port))
249 device_removed = true;
250 if (dwInsertions & (1 << port))
251 device_inserted = true;
252
253 if (device_removed)
254 {
255 /* if the controller was removed after
256 * XGetDeviceChanges but before
257 * XInputOpen, the device handle will be NULL. */
258 if (g_xinput_states[port].id)
259 XInputClose(g_xinput_states[port].id);
260
261 g_xinput_states[port].id = 0;
262
263 input_autoconfigure_disconnect(port, xdk_joypad.ident);
264 }
265
266 if (device_inserted)
267 {
268 XINPUT_POLLING_PARAMETERS m_pollingParameters;
269
270 m_pollingParameters.fAutoPoll = FALSE;
271 m_pollingParameters.fInterruptOut = TRUE;
272 m_pollingParameters.bInputInterval = 8;
273 m_pollingParameters.bOutputInterval = 8;
274
275 g_xinput_states[port].id = XInputOpen(
276 XDEVICE_TYPE_GAMEPAD, port,
277 XDEVICE_NO_SLOT, &m_pollingParameters);
278
279 xdk_joypad_autodetect_add(port);
280 }
281
282 if (!g_xinput_states[port].id)
283 continue;
284
285 /* if the controller is removed after
286 * XGetDeviceChanges but before XInputOpen,
287 * the device handle will be NULL. */
288 if (XInputPoll(g_xinput_states[port].id) != ERROR_SUCCESS)
289 continue;
290
291 memset(&g_xinput_states[port], 0, sizeof(xinput_joypad_state));
292
293 g_xinput_states[port].connected = !
294 (XInputGetState(
295 g_xinput_states[port].id
296 , &g_xinput_states[port].xstate) == ERROR_DEVICE_NOT_CONNECTED);
297 }
298 }
299
xdk_joypad_query_pad(unsigned pad)300 static bool xdk_joypad_query_pad(unsigned pad)
301 {
302 return pad < MAX_USERS && g_xinput_states[pad].connected;
303 }
304
xdk_joypad_destroy(void)305 static void xdk_joypad_destroy(void)
306 {
307 unsigned i;
308
309 for (i = 0; i < DEFAULT_MAX_PADS; i++)
310 {
311 memset(&g_xinput_states[i], 0, sizeof(xinput_joypad_state));
312 if (g_xinput_states[i].id)
313 XInputClose(g_xinput_states[i].id);
314 g_xinput_states[i].id = 0;
315 }
316 }
317
318 input_device_driver_t xdk_joypad = {
319 xdk_joypad_init,
320 xdk_joypad_query_pad,
321 xdk_joypad_destroy,
322 xdk_joypad_button,
323 xdk_joypad_state,
324 NULL,
325 xdk_joypad_axis,
326 xdk_joypad_poll,
327 NULL,
328 xdk_joypad_name,
329 "xdk",
330 };
331