e14743d1 |
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, ®size); |
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, ®size); |
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, ®size); |
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 */ |