controllermap.c (32227B)
1 /* 2 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11 */ 12 13 /* Game controller mapping generator */ 14 /* Gabriel Jacobo <gabomdq@gmail.com> */ 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include "SDL.h" 21 22 #ifndef SDL_JOYSTICK_DISABLED 23 24 /* Define this for verbose output while mapping controllers */ 25 #define DEBUG_CONTROLLERMAP 26 27 #define SCREEN_WIDTH 512 28 #define SCREEN_HEIGHT 320 29 30 #define MARKER_BUTTON 1 31 #define MARKER_AXIS 2 32 33 enum 34 { 35 SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, 36 SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, 37 SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, 38 SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, 39 SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, 40 SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, 41 SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, 42 SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, 43 SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, 44 SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, 45 SDL_CONTROLLER_BINDING_AXIS_MAX, 46 }; 47 48 #define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX) 49 50 static struct 51 { 52 int x, y; 53 double angle; 54 int marker; 55 56 } s_arrBindingDisplay[BINDING_COUNT] = { 57 { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */ 58 { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */ 59 { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */ 60 { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */ 61 { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */ 62 { 232, 128, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */ 63 { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */ 64 { 75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */ 65 { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */ 66 { 77, 40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */ 67 { 396, 36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */ 68 { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */ 69 { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */ 70 { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */ 71 { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */ 72 { 232, 174, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_MISC1 */ 73 { 132, 135, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE1 */ 74 { 330, 135, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE2 */ 75 { 132, 175, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE3 */ 76 { 330, 175, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_PADDLE4 */ 77 { 0, 0, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_TOUCHPAD */ 78 { 74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */ 79 { 74, 153, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */ 80 { 74, 153, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */ 81 { 74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */ 82 { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */ 83 { 306, 231, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */ 84 { 306, 231, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */ 85 { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */ 86 { 91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */ 87 { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */ 88 }; 89 90 static int s_arrBindingOrder[BINDING_COUNT] = { 91 SDL_CONTROLLER_BUTTON_A, 92 SDL_CONTROLLER_BUTTON_B, 93 SDL_CONTROLLER_BUTTON_Y, 94 SDL_CONTROLLER_BUTTON_X, 95 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, 96 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, 97 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, 98 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, 99 SDL_CONTROLLER_BUTTON_LEFTSTICK, 100 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, 101 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, 102 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, 103 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, 104 SDL_CONTROLLER_BUTTON_RIGHTSTICK, 105 SDL_CONTROLLER_BUTTON_LEFTSHOULDER, 106 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, 107 SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, 108 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, 109 SDL_CONTROLLER_BUTTON_DPAD_UP, 110 SDL_CONTROLLER_BUTTON_DPAD_RIGHT, 111 SDL_CONTROLLER_BUTTON_DPAD_DOWN, 112 SDL_CONTROLLER_BUTTON_DPAD_LEFT, 113 SDL_CONTROLLER_BUTTON_BACK, 114 SDL_CONTROLLER_BUTTON_GUIDE, 115 SDL_CONTROLLER_BUTTON_START, 116 SDL_CONTROLLER_BUTTON_MISC1, 117 SDL_CONTROLLER_BUTTON_PADDLE1, 118 SDL_CONTROLLER_BUTTON_PADDLE2, 119 SDL_CONTROLLER_BUTTON_PADDLE3, 120 SDL_CONTROLLER_BUTTON_PADDLE4, 121 }; 122 123 typedef struct 124 { 125 SDL_GameControllerBindType bindType; 126 union 127 { 128 int button; 129 130 struct { 131 int axis; 132 int axis_min; 133 int axis_max; 134 } axis; 135 136 struct { 137 int hat; 138 int hat_mask; 139 } hat; 140 141 } value; 142 143 SDL_bool committed; 144 145 } SDL_GameControllerExtendedBind; 146 147 static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT]; 148 149 typedef struct 150 { 151 SDL_bool m_bMoving; 152 int m_nLastValue; 153 int m_nStartingValue; 154 int m_nFarthestValue; 155 } AxisState; 156 157 static int s_nNumAxes; 158 static AxisState *s_arrAxisState; 159 160 static int s_iCurrentBinding; 161 static Uint32 s_unPendingAdvanceTime; 162 static SDL_bool s_bBindingComplete; 163 164 static SDL_Window *window; 165 static SDL_bool done = SDL_FALSE; 166 167 SDL_Texture * 168 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) 169 { 170 SDL_Surface *temp; 171 SDL_Texture *texture; 172 173 /* Load the sprite image */ 174 temp = SDL_LoadBMP(file); 175 if (temp == NULL) { 176 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError()); 177 return NULL; 178 } 179 180 /* Set transparent pixel as the pixel at (0,0) */ 181 if (transparent) { 182 if (temp->format->palette) { 183 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels); 184 } 185 } 186 187 /* Create textures from the image */ 188 texture = SDL_CreateTextureFromSurface(renderer, temp); 189 if (!texture) { 190 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError()); 191 SDL_FreeSurface(temp); 192 return NULL; 193 } 194 SDL_FreeSurface(temp); 195 196 /* We're ready to roll. :) */ 197 return texture; 198 } 199 200 static int 201 StandardizeAxisValue(int nValue) 202 { 203 if (nValue > SDL_JOYSTICK_AXIS_MAX/2) { 204 return SDL_JOYSTICK_AXIS_MAX; 205 } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) { 206 return SDL_JOYSTICK_AXIS_MIN; 207 } else { 208 return 0; 209 } 210 } 211 212 static void 213 SetCurrentBinding(int iBinding) 214 { 215 int iIndex; 216 SDL_GameControllerExtendedBind *pBinding; 217 218 if (iBinding < 0) { 219 return; 220 } 221 222 if (iBinding == BINDING_COUNT) { 223 s_bBindingComplete = SDL_TRUE; 224 return; 225 } 226 227 s_iCurrentBinding = iBinding; 228 229 pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]]; 230 SDL_zerop(pBinding); 231 232 for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) { 233 s_arrAxisState[iIndex].m_nFarthestValue = s_arrAxisState[iIndex].m_nStartingValue; 234 } 235 236 s_unPendingAdvanceTime = 0; 237 } 238 239 static SDL_bool 240 BBindingContainsBinding(const SDL_GameControllerExtendedBind *pBindingA, const SDL_GameControllerExtendedBind *pBindingB) 241 { 242 if (pBindingA->bindType != pBindingB->bindType) 243 { 244 return SDL_FALSE; 245 } 246 switch (pBindingA->bindType) 247 { 248 case SDL_CONTROLLER_BINDTYPE_AXIS: 249 if (pBindingA->value.axis.axis != pBindingB->value.axis.axis) { 250 return SDL_FALSE; 251 } 252 if (!pBindingA->committed) { 253 return SDL_FALSE; 254 } 255 { 256 int minA = SDL_min(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); 257 int maxA = SDL_max(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); 258 int minB = SDL_min(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); 259 int maxB = SDL_max(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); 260 return (minA <= minB && maxA >= maxB); 261 } 262 /* Not reached */ 263 default: 264 return SDL_memcmp(pBindingA, pBindingB, sizeof(*pBindingA)) == 0; 265 } 266 } 267 268 static void 269 ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding) 270 { 271 SDL_GameControllerExtendedBind *pCurrent; 272 int iIndex; 273 int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding]; 274 275 /* Do we already have this binding? */ 276 for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { 277 pCurrent = &s_arrBindings[iIndex]; 278 if (BBindingContainsBinding(pCurrent, pBinding)) { 279 if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) { 280 /* Skip to the next binding */ 281 SetCurrentBinding(s_iCurrentBinding + 1); 282 return; 283 } 284 285 if (iIndex == SDL_CONTROLLER_BUTTON_B) { 286 /* Go back to the previous binding */ 287 SetCurrentBinding(s_iCurrentBinding - 1); 288 return; 289 } 290 291 /* Already have this binding, ignore it */ 292 return; 293 } 294 } 295 296 #ifdef DEBUG_CONTROLLERMAP 297 switch ( pBinding->bindType ) 298 { 299 case SDL_CONTROLLER_BINDTYPE_NONE: 300 break; 301 case SDL_CONTROLLER_BINDTYPE_BUTTON: 302 SDL_Log("Configuring button binding for button %d\n", pBinding->value.button); 303 break; 304 case SDL_CONTROLLER_BINDTYPE_AXIS: 305 SDL_Log("Configuring axis binding for axis %d %d/%d committed = %s\n", pBinding->value.axis.axis, pBinding->value.axis.axis_min, pBinding->value.axis.axis_max, pBinding->committed ? "true" : "false"); 306 break; 307 case SDL_CONTROLLER_BINDTYPE_HAT: 308 SDL_Log("Configuring hat binding for hat %d %d\n", pBinding->value.hat.hat, pBinding->value.hat.hat_mask); 309 break; 310 } 311 #endif /* DEBUG_CONTROLLERMAP */ 312 313 /* Should the new binding override the existing one? */ 314 pCurrent = &s_arrBindings[iCurrentElement]; 315 if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) { 316 SDL_bool bNativeDPad, bCurrentDPad; 317 SDL_bool bNativeAxis, bCurrentAxis; 318 319 bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP || 320 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN || 321 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT || 322 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT); 323 bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT); 324 if (bNativeDPad && bCurrentDPad) { 325 /* We already have a binding of the type we want, ignore the new one */ 326 return; 327 } 328 329 bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX); 330 bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS); 331 if (bNativeAxis == bCurrentAxis && 332 (pBinding->bindType != SDL_CONTROLLER_BINDTYPE_AXIS || 333 pBinding->value.axis.axis != pCurrent->value.axis.axis)) { 334 /* We already have a binding of the type we want, ignore the new one */ 335 return; 336 } 337 } 338 339 *pCurrent = *pBinding; 340 341 if (pBinding->committed) { 342 s_unPendingAdvanceTime = SDL_GetTicks(); 343 } else { 344 s_unPendingAdvanceTime = 0; 345 } 346 } 347 348 static SDL_bool 349 BMergeAxisBindings(int iIndex) 350 { 351 SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex]; 352 SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1]; 353 if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && 354 pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && 355 pBindingA->value.axis.axis == pBindingB->value.axis.axis) { 356 if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) { 357 pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max; 358 pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max; 359 pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE; 360 return SDL_TRUE; 361 } 362 } 363 return SDL_FALSE; 364 } 365 366 static void 367 WatchJoystick(SDL_Joystick * joystick) 368 { 369 SDL_Renderer *screen = NULL; 370 SDL_Texture *background_front, *background_back, *button, *axis, *marker; 371 const char *name = NULL; 372 SDL_Event event; 373 SDL_Rect dst; 374 Uint8 alpha=200, alpha_step = -1; 375 Uint32 alpha_ticks = 0; 376 SDL_JoystickID nJoystickID; 377 378 screen = SDL_CreateRenderer(window, -1, 0); 379 if (screen == NULL) { 380 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); 381 return; 382 } 383 384 background_front = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); 385 background_back = LoadTexture(screen, "controllermap_back.bmp", SDL_FALSE); 386 button = LoadTexture(screen, "button.bmp", SDL_TRUE); 387 axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); 388 SDL_RaiseWindow(window); 389 390 /* scale for platforms that don't give you the window size you asked for. */ 391 SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); 392 393 /* Print info about the joystick we are watching */ 394 name = SDL_JoystickName(joystick); 395 SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick), 396 name ? name : "Unknown Joystick"); 397 SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n", 398 SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), 399 SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick)); 400 401 SDL_Log("\n\n\ 402 ====================================================================================\n\ 403 Press the buttons on your controller when indicated\n\ 404 (Your controller may look different than the picture)\n\ 405 If you want to correct a mistake, press backspace or the back button on your device\n\ 406 To skip a button, press SPACE or click/touch the screen\n\ 407 To exit, press ESC\n\ 408 ====================================================================================\n"); 409 410 nJoystickID = SDL_JoystickInstanceID(joystick); 411 412 s_nNumAxes = SDL_JoystickNumAxes(joystick); 413 s_arrAxisState = (AxisState *)SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState)); 414 415 /* Skip any spurious events at start */ 416 while (SDL_PollEvent(&event) > 0) { 417 continue; 418 } 419 420 /* Loop, getting joystick events! */ 421 while (!done && !s_bBindingComplete) { 422 int iElement = s_arrBindingOrder[s_iCurrentBinding]; 423 424 switch (s_arrBindingDisplay[iElement].marker) { 425 case MARKER_AXIS: 426 marker = axis; 427 break; 428 case MARKER_BUTTON: 429 marker = button; 430 break; 431 default: 432 break; 433 } 434 435 dst.x = s_arrBindingDisplay[iElement].x; 436 dst.y = s_arrBindingDisplay[iElement].y; 437 SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h); 438 439 if (SDL_GetTicks() - alpha_ticks > 5) { 440 alpha_ticks = SDL_GetTicks(); 441 alpha += alpha_step; 442 if (alpha == 255) { 443 alpha_step = -1; 444 } 445 if (alpha < 128) { 446 alpha_step = 1; 447 } 448 } 449 450 SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); 451 SDL_RenderClear(screen); 452 if (s_arrBindingOrder[s_iCurrentBinding] >= SDL_CONTROLLER_BUTTON_PADDLE1 && 453 s_arrBindingOrder[s_iCurrentBinding] <= SDL_CONTROLLER_BUTTON_PADDLE4) { 454 SDL_RenderCopy(screen, background_back, NULL, NULL); 455 } else { 456 SDL_RenderCopy(screen, background_front, NULL, NULL); 457 } 458 SDL_SetTextureAlphaMod(marker, alpha); 459 SDL_SetTextureColorMod(marker, 10, 255, 21); 460 SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE); 461 SDL_RenderPresent(screen); 462 463 while (SDL_PollEvent(&event) > 0) { 464 switch (event.type) { 465 case SDL_JOYDEVICEREMOVED: 466 if (event.jaxis.which == nJoystickID) { 467 done = SDL_TRUE; 468 } 469 break; 470 case SDL_JOYAXISMOTION: 471 if (event.jaxis.which == nJoystickID) { 472 const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 controller needed 96 */ 473 AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis]; 474 int nValue = event.jaxis.value; 475 int nCurrentDistance, nFarthestDistance; 476 if (!pAxisState->m_bMoving) { 477 Sint16 nInitialValue; 478 pAxisState->m_bMoving = SDL_JoystickGetAxisInitialState(joystick, event.jaxis.axis, &nInitialValue); 479 pAxisState->m_nLastValue = nValue; 480 pAxisState->m_nStartingValue = nInitialValue; 481 pAxisState->m_nFarthestValue = nInitialValue; 482 } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) { 483 break; 484 } else { 485 pAxisState->m_nLastValue = nValue; 486 } 487 nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue); 488 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); 489 if (nCurrentDistance > nFarthestDistance) { 490 pAxisState->m_nFarthestValue = nValue; 491 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); 492 } 493 494 #ifdef DEBUG_CONTROLLERMAP 495 SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance); 496 #endif 497 if (nFarthestDistance >= 16000) { 498 /* If we've gone out far enough and started to come back, let's bind this axis */ 499 SDL_bool bCommitBinding = (nCurrentDistance <= 10000) ? SDL_TRUE : SDL_FALSE; 500 SDL_GameControllerExtendedBind binding; 501 SDL_zero(binding); 502 binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; 503 binding.value.axis.axis = event.jaxis.axis; 504 binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue); 505 binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue); 506 binding.committed = bCommitBinding; 507 ConfigureBinding(&binding); 508 } 509 } 510 break; 511 case SDL_JOYHATMOTION: 512 if (event.jhat.which == nJoystickID) { 513 if (event.jhat.value != SDL_HAT_CENTERED) { 514 SDL_GameControllerExtendedBind binding; 515 516 #ifdef DEBUG_CONTROLLERMAP 517 SDL_Log("HAT %d %d\n", event.jhat.hat, event.jhat.value); 518 #endif 519 SDL_zero(binding); 520 binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT; 521 binding.value.hat.hat = event.jhat.hat; 522 binding.value.hat.hat_mask = event.jhat.value; 523 binding.committed = SDL_TRUE; 524 ConfigureBinding(&binding); 525 } 526 } 527 break; 528 case SDL_JOYBALLMOTION: 529 break; 530 case SDL_JOYBUTTONDOWN: 531 if (event.jbutton.which == nJoystickID) { 532 SDL_GameControllerExtendedBind binding; 533 534 #ifdef DEBUG_CONTROLLERMAP 535 SDL_Log("BUTTON %d\n", event.jbutton.button); 536 #endif 537 SDL_zero(binding); 538 binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; 539 binding.value.button = event.jbutton.button; 540 binding.committed = SDL_TRUE; 541 ConfigureBinding(&binding); 542 } 543 break; 544 case SDL_FINGERDOWN: 545 case SDL_MOUSEBUTTONDOWN: 546 /* Skip this step */ 547 SetCurrentBinding(s_iCurrentBinding + 1); 548 break; 549 case SDL_KEYDOWN: 550 if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) { 551 SetCurrentBinding(s_iCurrentBinding - 1); 552 break; 553 } 554 if (event.key.keysym.sym == SDLK_SPACE) { 555 SetCurrentBinding(s_iCurrentBinding + 1); 556 break; 557 } 558 559 if ((event.key.keysym.sym != SDLK_ESCAPE)) { 560 break; 561 } 562 /* Fall through to signal quit */ 563 case SDL_QUIT: 564 done = SDL_TRUE; 565 break; 566 default: 567 break; 568 } 569 } 570 571 SDL_Delay(15); 572 573 /* Wait 100 ms for joystick events to stop coming in, 574 in case a controller sends multiple events for a single control (e.g. axis and button for trigger) 575 */ 576 if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) { 577 SetCurrentBinding(s_iCurrentBinding + 1); 578 } 579 } 580 581 if (s_bBindingComplete) { 582 char mapping[1024]; 583 char trimmed_name[128]; 584 char *spot; 585 int iIndex; 586 char pszElement[12]; 587 588 SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name)); 589 while (SDL_isspace(trimmed_name[0])) { 590 SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name)); 591 } 592 while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) { 593 trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0'; 594 } 595 while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) { 596 SDL_memmove(spot, spot + 1, SDL_strlen(spot)); 597 } 598 599 /* Initialize mapping with GUID and name */ 600 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping)); 601 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 602 SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping)); 603 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 604 SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping)); 605 SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping)); 606 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 607 608 for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { 609 SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex]; 610 if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) { 611 continue; 612 } 613 614 if (iIndex < SDL_CONTROLLER_BUTTON_MAX) { 615 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex; 616 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping)); 617 } else { 618 const char *pszAxisName; 619 switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) { 620 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE: 621 if (!BMergeAxisBindings(iIndex)) { 622 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 623 } 624 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); 625 break; 626 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE: 627 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 628 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); 629 break; 630 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE: 631 if (!BMergeAxisBindings(iIndex)) { 632 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 633 } 634 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); 635 break; 636 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE: 637 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 638 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); 639 break; 640 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE: 641 if (!BMergeAxisBindings(iIndex)) { 642 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 643 } 644 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); 645 break; 646 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE: 647 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 648 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); 649 break; 650 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE: 651 if (!BMergeAxisBindings(iIndex)) { 652 SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); 653 } 654 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); 655 break; 656 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE: 657 SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); 658 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); 659 break; 660 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT: 661 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT); 662 break; 663 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT: 664 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT); 665 break; 666 } 667 SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping)); 668 } 669 SDL_strlcat(mapping, ":", SDL_arraysize(mapping)); 670 671 pszElement[0] = '\0'; 672 switch (pBinding->bindType) { 673 case SDL_CONTROLLER_BINDTYPE_BUTTON: 674 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button); 675 break; 676 case SDL_CONTROLLER_BINDTYPE_AXIS: 677 if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) { 678 /* The negative half axis */ 679 SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis); 680 } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) { 681 /* The positive half axis */ 682 SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis); 683 } else { 684 SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis); 685 if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) { 686 /* Invert the axis */ 687 SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement)); 688 } 689 } 690 break; 691 case SDL_CONTROLLER_BINDTYPE_HAT: 692 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask); 693 break; 694 default: 695 SDL_assert(!"Unknown bind type"); 696 break; 697 } 698 SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping)); 699 SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); 700 } 701 702 SDL_Log("Mapping:\n\n%s\n\n", mapping); 703 /* Print to stdout as well so the user can cat the output somewhere */ 704 printf("%s\n", mapping); 705 } 706 707 SDL_free(s_arrAxisState); 708 s_arrAxisState = NULL; 709 710 SDL_DestroyRenderer(screen); 711 } 712 713 int 714 main(int argc, char *argv[]) 715 { 716 const char *name; 717 int i; 718 SDL_Joystick *joystick; 719 720 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); 721 722 /* Enable standard application logging */ 723 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); 724 725 /* Initialize SDL (Note: video is required to start event loop) */ 726 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 727 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); 728 exit(1); 729 } 730 731 /* Create a window to display joystick axis position */ 732 window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED, 733 SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, 734 SCREEN_HEIGHT, 0); 735 if (window == NULL) { 736 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); 737 return 2; 738 } 739 740 while (SDL_NumJoysticks() == 0) { 741 SDL_Event event; 742 743 while (SDL_PollEvent(&event) > 0) { 744 switch (event.type) { 745 case SDL_KEYDOWN: 746 if ((event.key.keysym.sym != SDLK_ESCAPE)) { 747 break; 748 } 749 /* Fall through to signal quit */ 750 case SDL_QUIT: 751 done = SDL_TRUE; 752 break; 753 default: 754 break; 755 } 756 } 757 } 758 759 /* Print information about the joysticks */ 760 SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks()); 761 for (i = 0; i < SDL_NumJoysticks(); ++i) { 762 name = SDL_JoystickNameForIndex(i); 763 SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick"); 764 joystick = SDL_JoystickOpen(i); 765 if (joystick == NULL) { 766 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i, 767 SDL_GetError()); 768 } else { 769 char guid[64]; 770 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), 771 guid, sizeof (guid)); 772 SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick)); 773 SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick)); 774 SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick)); 775 SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); 776 SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick)); 777 SDL_Log(" guid: %s\n", guid); 778 SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); 779 SDL_JoystickClose(joystick); 780 } 781 } 782 783 joystick = SDL_JoystickOpen(0); 784 if (joystick == NULL) { 785 SDL_Log("Couldn't open joystick 0: %s\n", SDL_GetError()); 786 } else { 787 WatchJoystick(joystick); 788 SDL_JoystickClose(joystick); 789 } 790 791 SDL_DestroyWindow(window); 792 793 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); 794 795 return 0; 796 } 797 798 #else 799 800 int 801 main(int argc, char *argv[]) 802 { 803 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n"); 804 return 1; 805 } 806 807 #endif 808 809 /* vi: set ts=4 sw=4 expandtab: */