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, &regsize);
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, &regsize);
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, &regsize);
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