| 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 */ |