SDL-1.2.14
[sdl_omap.git] / src / joystick / win32 / SDL_mmjoystick.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 #ifdef SDL_JOYSTICK_WINMM
25
26 /* Win32 MultiMedia Joystick driver, contributed by Andrei de A. Formiga */
27
28 #define WIN32_LEAN_AND_MEAN
29 #include <windows.h>
30 #include <mmsystem.h>
31 #include <regstr.h>
32
33 #include "SDL_events.h"
34 #include "SDL_joystick.h"
35 #include "../SDL_sysjoystick.h"
36 #include "../SDL_joystick_c.h"
37
38 #define MAX_JOYSTICKS   16
39 #define MAX_AXES        6       /* each joystick can have up to 6 axes */
40 #define MAX_BUTTONS     32      /* and 32 buttons                      */
41 #define AXIS_MIN        -32768  /* minimum value for axis coordinate */
42 #define AXIS_MAX        32767   /* maximum value for axis coordinate */
43 /* limit axis to 256 possible positions to filter out noise */
44 #define JOY_AXIS_THRESHOLD      (((AXIS_MAX)-(AXIS_MIN))/256)
45 #define JOY_BUTTON_FLAG(n)      (1<<n)
46
47
48 /* array to hold joystick ID values */
49 static UINT     SYS_JoystickID[MAX_JOYSTICKS];
50 static JOYCAPS  SYS_Joystick[MAX_JOYSTICKS];
51 static char     *SYS_JoystickName[MAX_JOYSTICKS];
52
53 /* The private structure used to keep track of a joystick */
54 struct joystick_hwdata
55 {
56         /* joystick ID */
57         UINT    id;
58
59         /* values used to translate device-specific coordinates into
60            SDL-standard ranges */
61         struct _transaxis {
62                 int offset;
63                 float scale;
64         } transaxis[6];
65 };
66
67 /* Convert a win32 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 {
73         /* added 7/24/2004 by Eckhard Stolberg */
74         /*
75                 see if there is a joystick for the current
76                 index (1-16) listed in the registry
77         */
78         char *name = NULL;
79         HKEY hTopKey;
80         HKEY hKey;
81         DWORD regsize;
82         LONG regresult;
83         char regkey[256];
84         char regvalue[256];
85         char regname[256];
86
87         SDL_snprintf(regkey, SDL_arraysize(regkey), "%s\\%s\\%s",
88                 REGSTR_PATH_JOYCONFIG, szRegKey, REGSTR_KEY_JOYCURR);
89         hTopKey = HKEY_LOCAL_MACHINE;
90         regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
91         if (regresult != ERROR_SUCCESS) {
92                 hTopKey = HKEY_CURRENT_USER;
93                 regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
94         }
95         if (regresult != ERROR_SUCCESS) {
96                 return NULL;
97         }
98
99         /* find the registry key name for the joystick's properties */
100         regsize = sizeof(regname);
101         SDL_snprintf(regvalue, SDL_arraysize(regvalue), "Joystick%d%s", index+1, REGSTR_VAL_JOYOEMNAME);
102         regresult = RegQueryValueExA(hKey, regvalue, 0, 0, (LPBYTE)regname, &regsize);
103         RegCloseKey(hKey);
104
105         if (regresult != ERROR_SUCCESS) {
106                 return NULL;
107         }
108
109         /* open that registry key */
110         SDL_snprintf(regkey, SDL_arraysize(regkey), "%s\\%s", REGSTR_PATH_JOYOEM, regname);
111         regresult = RegOpenKeyExA(hTopKey, regkey, 0, KEY_READ, &hKey);
112         if (regresult != ERROR_SUCCESS) {
113                 return NULL;
114         }
115
116         /* find the size for the OEM name text */
117         regsize = sizeof(regvalue);
118         regresult = RegQueryValueExA(hKey, REGSTR_VAL_JOYOEMNAME, 0, 0, NULL, &regsize);
119         if (regresult == ERROR_SUCCESS) {
120                 /* allocate enough memory for the OEM name text ... */
121                 name = (char *) SDL_malloc(regsize);
122                 if ( name ) {
123                         /* ... and read it from the registry */
124                         regresult = RegQueryValueExA(hKey,
125                                 REGSTR_VAL_JOYOEMNAME, 0, 0,
126                                 (LPBYTE) name, &regsize);
127                 }
128         }
129         RegCloseKey(hKey);
130
131         return(name);
132 }
133
134 /* Function to scan the system for joysticks.
135  * This function should set SDL_numjoysticks to the number of available
136  * joysticks.  Joystick 0 should be the system default joystick.
137  * It should return 0, or -1 on an unrecoverable fatal error.
138  */
139 int SDL_SYS_JoystickInit(void)
140 {
141         int     i;
142         int maxdevs;
143         int numdevs;
144         JOYINFOEX joyinfo;
145         JOYCAPS joycaps;
146         MMRESULT result;
147
148         /* Reset the joystick ID & name mapping tables */
149         for ( i = 0; i < MAX_JOYSTICKS; ++i ) {
150                 SYS_JoystickID[i] = 0;
151                 SYS_JoystickName[i] = NULL;
152         }
153
154         /* Loop over all potential joystick devices */
155         numdevs = 0;
156         maxdevs = joyGetNumDevs();
157         for ( i = JOYSTICKID1; i < maxdevs && numdevs < MAX_JOYSTICKS; ++i ) {
158                 
159                 joyinfo.dwSize = sizeof(joyinfo);
160                 joyinfo.dwFlags = JOY_RETURNALL;
161                 result = joyGetPosEx(i, &joyinfo);
162                 if ( result == JOYERR_NOERROR ) {
163                         result = joyGetDevCaps(i, &joycaps, sizeof(joycaps));
164                         if ( result == JOYERR_NOERROR ) {
165                                 SYS_JoystickID[numdevs] = i;
166                                 SYS_Joystick[numdevs] = joycaps;
167                                 SYS_JoystickName[numdevs] = GetJoystickName(i, joycaps.szRegKey);
168                                 numdevs++;
169                         }
170                 }
171         }
172         return(numdevs);
173 }
174
175 /* Function to get the device-dependent name of a joystick */
176 const char *SDL_SYS_JoystickName(int index)
177 {
178         if ( SYS_JoystickName[index] != NULL ) {
179                 return(SYS_JoystickName[index]);
180         } else {
181                 return(SYS_Joystick[index].szPname);
182         }
183 }
184
185 /* Function to open a joystick for use.
186    The joystick to open is specified by the index field of the joystick.
187    This should fill the nbuttons and naxes fields of the joystick structure.
188    It returns 0, or -1 if there is an error.
189  */
190 int SDL_SYS_JoystickOpen(SDL_Joystick *joystick)
191 {
192         int index, i;
193         int caps_flags[MAX_AXES-2] =
194                 { JOYCAPS_HASZ, JOYCAPS_HASR, JOYCAPS_HASU, JOYCAPS_HASV };
195         int axis_min[MAX_AXES], axis_max[MAX_AXES];
196
197
198         /* shortcut */
199         index = joystick->index;
200         axis_min[0] = SYS_Joystick[index].wXmin;
201         axis_max[0] = SYS_Joystick[index].wXmax;
202         axis_min[1] = SYS_Joystick[index].wYmin;
203         axis_max[1] = SYS_Joystick[index].wYmax;
204         axis_min[2] = SYS_Joystick[index].wZmin;
205         axis_max[2] = SYS_Joystick[index].wZmax;
206         axis_min[3] = SYS_Joystick[index].wRmin;
207         axis_max[3] = SYS_Joystick[index].wRmax;
208         axis_min[4] = SYS_Joystick[index].wUmin;
209         axis_max[4] = SYS_Joystick[index].wUmax;
210         axis_min[5] = SYS_Joystick[index].wVmin;
211         axis_max[5] = SYS_Joystick[index].wVmax;
212
213         /* allocate memory for system specific hardware data */
214         joystick->hwdata = (struct joystick_hwdata *) SDL_malloc(sizeof(*joystick->hwdata));
215         if (joystick->hwdata == NULL)
216         {
217                 SDL_OutOfMemory();
218                 return(-1);
219         }
220         SDL_memset(joystick->hwdata, 0, sizeof(*joystick->hwdata));
221
222         /* set hardware data */
223         joystick->hwdata->id = SYS_JoystickID[index];
224         for ( i = 0; i < MAX_AXES; ++i ) {
225                 if ( (i<2) || (SYS_Joystick[index].wCaps & caps_flags[i-2]) ) {
226                         joystick->hwdata->transaxis[i].offset =
227                                 AXIS_MIN - axis_min[i];
228                         joystick->hwdata->transaxis[i].scale =
229                                 (float)(AXIS_MAX - AXIS_MIN) / (axis_max[i] - axis_min[i]);
230                 } else {
231                         joystick->hwdata->transaxis[i].offset = 0;
232                         joystick->hwdata->transaxis[i].scale = 1.0; /* Just in case */
233                 }
234         }
235
236         /* fill nbuttons, naxes, and nhats fields */
237         joystick->nbuttons = SYS_Joystick[index].wNumButtons;
238         joystick->naxes = SYS_Joystick[index].wNumAxes;
239         if ( SYS_Joystick[index].wCaps & JOYCAPS_HASPOV ) {
240                 joystick->nhats = 1;
241         } else {
242                 joystick->nhats = 0;
243         }
244         return(0);
245 }
246
247 static Uint8 TranslatePOV(DWORD value)
248 {
249         Uint8 pos;
250
251         pos = SDL_HAT_CENTERED;
252         if ( value != JOY_POVCENTERED ) {
253                 if ( (value > JOY_POVLEFT) || (value < JOY_POVRIGHT) ) {
254                         pos |= SDL_HAT_UP;
255                 }
256                 if ( (value > JOY_POVFORWARD) && (value < JOY_POVBACKWARD) ) {
257                         pos |= SDL_HAT_RIGHT;
258                 }
259                 if ( (value > JOY_POVRIGHT) && (value < JOY_POVLEFT) ) {
260                         pos |= SDL_HAT_DOWN;
261                 }
262                 if ( value > JOY_POVBACKWARD ) {
263                         pos |= SDL_HAT_LEFT;
264                 }
265         }
266         return(pos);
267 }
268
269 /* Function to update the state of a joystick - called as a device poll.
270  * This function shouldn't update the joystick structure directly,
271  * but instead should call SDL_PrivateJoystick*() to deliver events
272  * and update joystick device state.
273  */
274 void SDL_SYS_JoystickUpdate(SDL_Joystick *joystick)
275 {
276         MMRESULT result;
277         int i;
278         DWORD flags[MAX_AXES] = { JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, 
279                                   JOY_RETURNR, JOY_RETURNU, JOY_RETURNV };
280         DWORD pos[MAX_AXES];
281         struct _transaxis *transaxis;
282         int value, change;
283         JOYINFOEX joyinfo;
284
285         joyinfo.dwSize = sizeof(joyinfo);
286         joyinfo.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS;
287         if ( ! joystick->hats ) {
288                 joyinfo.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS);
289         }
290         result = joyGetPosEx(joystick->hwdata->id, &joyinfo);
291         if ( result != JOYERR_NOERROR ) {
292                 SetMMerror("joyGetPosEx", result);
293                 return;
294         }
295
296         /* joystick motion events */
297         pos[0] = joyinfo.dwXpos;
298         pos[1] = joyinfo.dwYpos;
299         pos[2] = joyinfo.dwZpos;
300         pos[3] = joyinfo.dwRpos;
301         pos[4] = joyinfo.dwUpos;
302         pos[5] = joyinfo.dwVpos;
303
304         transaxis = joystick->hwdata->transaxis;
305         for (i = 0; i < joystick->naxes; i++) {
306                 if (joyinfo.dwFlags & flags[i]) {
307                         value = (int)(((float)pos[i] + transaxis[i].offset) * transaxis[i].scale);
308                         change = (value - joystick->axes[i]);
309                         if ( (change < -JOY_AXIS_THRESHOLD) || (change > JOY_AXIS_THRESHOLD) ) {
310                                 SDL_PrivateJoystickAxis(joystick, (Uint8)i, (Sint16)value);
311                         }
312                 }
313         }
314
315         /* joystick button events */
316         if ( joyinfo.dwFlags & JOY_RETURNBUTTONS ) {
317                 for ( i = 0; i < joystick->nbuttons; ++i ) {
318                         if ( joyinfo.dwButtons & JOY_BUTTON_FLAG(i) ) {
319                                 if ( ! joystick->buttons[i] ) {
320                                         SDL_PrivateJoystickButton(joystick, (Uint8)i, SDL_PRESSED);
321                                 }
322                         } else {
323                                 if ( joystick->buttons[i] ) {
324                                         SDL_PrivateJoystickButton(joystick, (Uint8)i, SDL_RELEASED);
325                                 }
326                         }
327                 }
328         }
329
330         /* joystick hat events */
331         if ( joyinfo.dwFlags & JOY_RETURNPOV ) {
332                 Uint8 pos;
333
334                 pos = TranslatePOV(joyinfo.dwPOV);
335                 if ( pos != joystick->hats[0] ) {
336                         SDL_PrivateJoystickHat(joystick, 0, pos);
337                 }
338         }
339 }
340
341 /* Function to close a joystick after use */
342 void SDL_SYS_JoystickClose(SDL_Joystick *joystick)
343 {
344         if (joystick->hwdata != NULL) {
345                 /* free system specific hardware data */
346                 SDL_free(joystick->hwdata);
347                 joystick->hwdata = NULL;
348         }
349 }
350
351 /* Function to perform any system-specific joystick related cleanup */
352 void SDL_SYS_JoystickQuit(void)
353 {
354         int i;
355         for (i = 0; i < MAX_JOYSTICKS; i++) {
356                 if ( SYS_JoystickName[i] != NULL ) {
357                         SDL_free(SYS_JoystickName[i]);
358                         SYS_JoystickName[i] = NULL;
359                 }
360         }
361 }
362
363
364 /* implementation functions */
365 void SetMMerror(char *function, int code)
366 {
367         static char *error;
368         static char  errbuf[1024];
369
370         errbuf[0] = 0;
371         switch (code) 
372         {
373                 case MMSYSERR_NODRIVER:
374                         error = "Joystick driver not present";
375                 break;
376
377                 case MMSYSERR_INVALPARAM:
378                 case JOYERR_PARMS:
379                         error = "Invalid parameter(s)";
380                 break;
381                 
382                 case MMSYSERR_BADDEVICEID:
383                         error = "Bad device ID";
384                 break;
385
386                 case JOYERR_UNPLUGGED:
387                         error = "Joystick not attached";
388                 break;
389
390                 case JOYERR_NOCANDO:
391                         error = "Can't capture joystick input";
392                 break;
393
394                 default:
395                         SDL_snprintf(errbuf, SDL_arraysize(errbuf),
396                                  "%s: Unknown Multimedia system error: 0x%x",
397                                                                 function, code);
398                 break;
399         }
400
401         if ( ! errbuf[0] ) {
402                 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, error);
403         }
404         SDL_SetError("%s", errbuf);
405 }
406
407 #endif /* SDL_JOYSTICK_WINMM */