1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #ifdef SDL_JOYSTICK_WINMM
24
25 /* Win32 MultiMedia Joystick driver, contributed by Andrei de A. Formiga */
26
27 #include "../../core/windows/SDL_windows.h"
28 #include <mmsystem.h>
29 #include <regstr.h>
30
31 #include "SDL_events.h"
32 #include "SDL_joystick.h"
33 #include "../SDL_sysjoystick.h"
34 #include "../SDL_joystick_c.h"
35
36 #ifdef REGSTR_VAL_JOYOEMNAME
37 #undef REGSTR_VAL_JOYOEMNAME
38 #endif
39 #define REGSTR_VAL_JOYOEMNAME "OEMName"
40
41 #define MAX_JOYSTICKS 16
42 #define MAX_AXES 6 /* each joystick can have up to 6 axes */
43 #define MAX_BUTTONS 32 /* and 32 buttons */
44 #define AXIS_MIN -32768 /* minimum value for axis coordinate */
45 #define AXIS_MAX 32767 /* maximum value for axis coordinate */
46 /* limit axis to 256 possible positions to filter out noise */
47 #define JOY_AXIS_THRESHOLD (((AXIS_MAX)-(AXIS_MIN))/256)
48 #define JOY_BUTTON_FLAG(n) (1<<n)
49
50
51 /* array to hold joystick ID values */
52 static UINT SYS_JoystickID[MAX_JOYSTICKS];
53 static JOYCAPSA SYS_Joystick[MAX_JOYSTICKS];
54 static char *SYS_JoystickName[MAX_JOYSTICKS];
55
56 /* The private structure used to keep track of a joystick */
57 struct joystick_hwdata
58 {
59 /* joystick ID */
60 UINT id;
61
62 /* values used to translate device-specific coordinates into
63 SDL-standard ranges */
64 struct _transaxis
65 {
66 int offset;
67 float scale;
68 } transaxis[6];
69 };
70
71 /* Convert a Windows Multimedia API return code to a text message */
72 static void SetMMerror(char *function, int code);
73
74
75 static char *
GetJoystickName(int index,const char * szRegKey)76 GetJoystickName(int index, const char *szRegKey)
77 {
78 /* added 7/24/2004 by Eckhard Stolberg */
79 /*
80 see if there is a joystick for the current
81 index (1-16) listed in the registry
82 */
83 char *name = NULL;
84 HKEY hTopKey;
85 HKEY hKey;
86 DWORD regsize;
87 LONG regresult;
88 char regkey[256];
89 char regvalue[256];
90 char regname[256];
91
92 SDL_snprintf(regkey, SDL_arraysize(regkey), "%s\\%s\\%s",
93 REGSTR_PATH_JOYCONFIG, szRegKey, REGSTR_KEY_JOYCURR);
94 hTopKey = HKEY_LOCAL_MACHINE;
95 regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
96 if (regresult != ERROR_SUCCESS) {
97 hTopKey = HKEY_CURRENT_USER;
98 regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
99 }
100 if (regresult != ERROR_SUCCESS) {
101 return NULL;
102 }
103
104 /* find the registry key name for the joystick's properties */
105 regsize = sizeof(regname);
106 SDL_snprintf(regvalue, SDL_arraysize(regvalue), "Joystick%d%s", index + 1,
107 REGSTR_VAL_JOYOEMNAME);
108 regresult =
109 RegQueryValueExA(hKey, regvalue, 0, 0, (LPBYTE) regname, ®size);
110 RegCloseKey(hKey);
111
112 if (regresult != ERROR_SUCCESS) {
113 return NULL;
114 }
115
116 /* open that registry key */
117 SDL_snprintf(regkey, SDL_arraysize(regkey), "%s\\%s", REGSTR_PATH_JOYOEM,
118 regname);
119 regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
120 if (regresult != ERROR_SUCCESS) {
121 return NULL;
122 }
123
124 /* find the size for the OEM name text */
125 regsize = sizeof(regvalue);
126 regresult =
127 RegQueryValueExA(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, NULL, ®size);
128 if (regresult == ERROR_SUCCESS) {
129 /* allocate enough memory for the OEM name text ... */
130 name = (char *) SDL_malloc(regsize);
131 if (name) {
132 /* ... and read it from the registry */
133 regresult = RegQueryValueExA(hKey,
134 REGSTR_VAL_JOYOEMNAME, 0, 0,
135 (LPBYTE) name, ®size);
136 }
137 }
138 RegCloseKey(hKey);
139
140 return (name);
141 }
142
143 static int SDL_SYS_numjoysticks = 0;
144
145 /* Function to scan the system for joysticks.
146 * Joystick 0 should be the system default joystick.
147 * It should return 0, or -1 on an unrecoverable fatal error.
148 */
149 int
SDL_SYS_JoystickInit(void)150 SDL_SYS_JoystickInit(void)
151 {
152 int i;
153 int maxdevs;
154 JOYINFOEX joyinfo;
155 JOYCAPSA joycaps;
156 MMRESULT result;
157
158 /* Reset the joystick ID & name mapping tables */
159 for (i = 0; i < MAX_JOYSTICKS; ++i) {
160 SYS_JoystickID[i] = 0;
161 SYS_JoystickName[i] = NULL;
162 }
163
164 /* Loop over all potential joystick devices */
165 SDL_SYS_numjoysticks = 0;
166 maxdevs = joyGetNumDevs();
167 for (i = JOYSTICKID1; i < maxdevs && SDL_SYS_numjoysticks < MAX_JOYSTICKS; ++i) {
168
169 joyinfo.dwSize = sizeof(joyinfo);
170 joyinfo.dwFlags = JOY_RETURNALL;
171 result = joyGetPosEx(i, &joyinfo);
172 if (result == JOYERR_NOERROR) {
173 result = joyGetDevCapsA(i, &joycaps, sizeof(joycaps));
174 if (result == JOYERR_NOERROR) {
175 SYS_JoystickID[SDL_SYS_numjoysticks] = i;
176 SYS_Joystick[SDL_SYS_numjoysticks] = joycaps;
177 SYS_JoystickName[SDL_SYS_numjoysticks] =
178 GetJoystickName(i, joycaps.szRegKey);
179 SDL_SYS_numjoysticks++;
180 }
181 }
182 }
183 return (SDL_SYS_numjoysticks);
184 }
185
SDL_SYS_NumJoysticks()186 int SDL_SYS_NumJoysticks()
187 {
188 return SDL_SYS_numjoysticks;
189 }
190
SDL_SYS_JoystickDetect()191 void SDL_SYS_JoystickDetect()
192 {
193 }
194
195 /* Function to get the device-dependent name of a joystick */
196 const char *
SDL_SYS_JoystickNameForDeviceIndex(int device_index)197 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
198 {
199 if (SYS_JoystickName[device_index] != NULL) {
200 return (SYS_JoystickName[device_index]);
201 } else {
202 return (SYS_Joystick[device_index].szPname);
203 }
204 }
205
206 /* Function to perform the mapping from device index to the instance id for this index */
SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)207 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
208 {
209 return device_index;
210 }
211
212 /* Function to open a joystick for use.
213 The joystick to open is specified by the device index.
214 This should fill the nbuttons and naxes fields of the joystick structure.
215 It returns 0, or -1 if there is an error.
216 */
217 int
SDL_SYS_JoystickOpen(SDL_Joystick * joystick,int device_index)218 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
219 {
220 int index, i;
221 int caps_flags[MAX_AXES - 2] =
222 { JOYCAPS_HASZ, JOYCAPS_HASR, JOYCAPS_HASU, JOYCAPS_HASV };
223 int axis_min[MAX_AXES], axis_max[MAX_AXES];
224
225
226 /* shortcut */
227 index = device_index;
228 axis_min[0] = SYS_Joystick[index].wXmin;
229 axis_max[0] = SYS_Joystick[index].wXmax;
230 axis_min[1] = SYS_Joystick[index].wYmin;
231 axis_max[1] = SYS_Joystick[index].wYmax;
232 axis_min[2] = SYS_Joystick[index].wZmin;
233 axis_max[2] = SYS_Joystick[index].wZmax;
234 axis_min[3] = SYS_Joystick[index].wRmin;
235 axis_max[3] = SYS_Joystick[index].wRmax;
236 axis_min[4] = SYS_Joystick[index].wUmin;
237 axis_max[4] = SYS_Joystick[index].wUmax;
238 axis_min[5] = SYS_Joystick[index].wVmin;
239 axis_max[5] = SYS_Joystick[index].wVmax;
240
241 /* allocate memory for system specific hardware data */
242 joystick->instance_id = device_index;
243 joystick->hwdata =
244 (struct joystick_hwdata *) SDL_malloc(sizeof(*joystick->hwdata));
245 if (joystick->hwdata == NULL) {
246 return SDL_OutOfMemory();
247 }
248 SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
249
250 /* set hardware data */
251 joystick->hwdata->id = SYS_JoystickID[index];
252 for (i = 0; i < MAX_AXES; ++i) {
253 if ((i < 2) || (SYS_Joystick[index].wCaps & caps_flags[i - 2])) {
254 joystick->hwdata->transaxis[i].offset = AXIS_MIN - axis_min[i];
255 joystick->hwdata->transaxis[i].scale =
256 (float) (AXIS_MAX - AXIS_MIN) / (axis_max[i] - axis_min[i]);
257 } else {
258 joystick->hwdata->transaxis[i].offset = 0;
259 joystick->hwdata->transaxis[i].scale = 1.0; /* Just in case */
260 }
261 }
262
263 /* fill nbuttons, naxes, and nhats fields */
264 joystick->nbuttons = SYS_Joystick[index].wNumButtons;
265 joystick->naxes = SYS_Joystick[index].wNumAxes;
266 if (SYS_Joystick[index].wCaps & JOYCAPS_HASPOV) {
267 joystick->nhats = 1;
268 } else {
269 joystick->nhats = 0;
270 }
271 return (0);
272 }
273
274 /* Function to determine if this joystick is attached to the system right now */
SDL_SYS_JoystickAttached(SDL_Joystick * joystick)275 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
276 {
277 return SDL_TRUE;
278 }
279
280 static Uint8
TranslatePOV(DWORD value)281 TranslatePOV(DWORD value)
282 {
283 Uint8 pos;
284
285 pos = SDL_HAT_CENTERED;
286 if (value != JOY_POVCENTERED) {
287 if ((value > JOY_POVLEFT) || (value < JOY_POVRIGHT)) {
288 pos |= SDL_HAT_UP;
289 }
290 if ((value > JOY_POVFORWARD) && (value < JOY_POVBACKWARD)) {
291 pos |= SDL_HAT_RIGHT;
292 }
293 if ((value > JOY_POVRIGHT) && (value < JOY_POVLEFT)) {
294 pos |= SDL_HAT_DOWN;
295 }
296 if (value > JOY_POVBACKWARD) {
297 pos |= SDL_HAT_LEFT;
298 }
299 }
300 return (pos);
301 }
302
303 /* Function to update the state of a joystick - called as a device poll.
304 * This function shouldn't update the joystick structure directly,
305 * but instead should call SDL_PrivateJoystick*() to deliver events
306 * and update joystick device state.
307 */
308 void
SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)309 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
310 {
311 MMRESULT result;
312 int i;
313 DWORD flags[MAX_AXES] = { JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ,
314 JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
315 };
316 DWORD pos[MAX_AXES];
317 struct _transaxis *transaxis;
318 int value, change;
319 JOYINFOEX joyinfo;
320
321 joyinfo.dwSize = sizeof(joyinfo);
322 joyinfo.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS;
323 if (!joystick->hats) {
324 joyinfo.dwFlags &= ~(JOY_RETURNPOV | JOY_RETURNPOVCTS);
325 }
326 result = joyGetPosEx(joystick->hwdata->id, &joyinfo);
327 if (result != JOYERR_NOERROR) {
328 SetMMerror("joyGetPosEx", result);
329 return;
330 }
331
332 /* joystick motion events */
333 pos[0] = joyinfo.dwXpos;
334 pos[1] = joyinfo.dwYpos;
335 pos[2] = joyinfo.dwZpos;
336 pos[3] = joyinfo.dwRpos;
337 pos[4] = joyinfo.dwUpos;
338 pos[5] = joyinfo.dwVpos;
339
340 transaxis = joystick->hwdata->transaxis;
341 for (i = 0; i < joystick->naxes; i++) {
342 if (joyinfo.dwFlags & flags[i]) {
343 value =
344 (int) (((float) pos[i] +
345 transaxis[i].offset) * transaxis[i].scale);
346 change = (value - joystick->axes[i]);
347 if ((change < -JOY_AXIS_THRESHOLD)
348 || (change > JOY_AXIS_THRESHOLD)) {
349 SDL_PrivateJoystickAxis(joystick, (Uint8) i, (Sint16) value);
350 }
351 }
352 }
353
354 /* joystick button events */
355 if (joyinfo.dwFlags & JOY_RETURNBUTTONS) {
356 for (i = 0; i < joystick->nbuttons; ++i) {
357 if (joyinfo.dwButtons & JOY_BUTTON_FLAG(i)) {
358 if (!joystick->buttons[i]) {
359 SDL_PrivateJoystickButton(joystick, (Uint8) i,
360 SDL_PRESSED);
361 }
362 } else {
363 if (joystick->buttons[i]) {
364 SDL_PrivateJoystickButton(joystick, (Uint8) i,
365 SDL_RELEASED);
366 }
367 }
368 }
369 }
370
371 /* joystick hat events */
372 if (joyinfo.dwFlags & JOY_RETURNPOV) {
373 Uint8 pos;
374
375 pos = TranslatePOV(joyinfo.dwPOV);
376 if (pos != joystick->hats[0]) {
377 SDL_PrivateJoystickHat(joystick, 0, pos);
378 }
379 }
380 }
381
382 /* Function to close a joystick after use */
383 void
SDL_SYS_JoystickClose(SDL_Joystick * joystick)384 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
385 {
386 SDL_free(joystick->hwdata);
387 }
388
389 /* Function to perform any system-specific joystick related cleanup */
390 void
SDL_SYS_JoystickQuit(void)391 SDL_SYS_JoystickQuit(void)
392 {
393 int i;
394 for (i = 0; i < MAX_JOYSTICKS; i++) {
395 SDL_free(SYS_JoystickName[i]);
396 SYS_JoystickName[i] = NULL;
397 }
398 }
399
SDL_SYS_JoystickGetDeviceGUID(int device_index)400 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
401 {
402 SDL_JoystickGUID guid;
403 /* the GUID is just the first 16 chars of the name for now */
404 const char *name = SDL_SYS_JoystickNameForDeviceIndex( device_index );
405 SDL_zero( guid );
406 SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
407 return guid;
408 }
409
SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)410 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
411 {
412 SDL_JoystickGUID guid;
413 /* the GUID is just the first 16 chars of the name for now */
414 const char *name = joystick->name;
415 SDL_zero( guid );
416 SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
417 return guid;
418 }
419
420
421 /* implementation functions */
422 void
SetMMerror(char * function,int code)423 SetMMerror(char *function, int code)
424 {
425 static char *error;
426 static char errbuf[1024];
427
428 errbuf[0] = 0;
429 switch (code) {
430 case MMSYSERR_NODRIVER:
431 error = "Joystick driver not present";
432 break;
433
434 case MMSYSERR_INVALPARAM:
435 case JOYERR_PARMS:
436 error = "Invalid parameter(s)";
437 break;
438
439 case MMSYSERR_BADDEVICEID:
440 error = "Bad device ID";
441 break;
442
443 case JOYERR_UNPLUGGED:
444 error = "Joystick not attached";
445 break;
446
447 case JOYERR_NOCANDO:
448 error = "Can't capture joystick input";
449 break;
450
451 default:
452 SDL_snprintf(errbuf, SDL_arraysize(errbuf),
453 "%s: Unknown Multimedia system error: 0x%x",
454 function, code);
455 break;
456 }
457
458 if (!errbuf[0]) {
459 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function,
460 error);
461 }
462 SDL_SetError("%s", errbuf);
463 }
464
465 #endif /* SDL_JOYSTICK_WINMM */
466
467 /* vi: set ts=4 sw=4 expandtab: */
468