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