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